侧边栏壁纸
博主头像
zyixin

当你觉得为时已晚的时候,恰恰是最早的时候

  • 累计撰写 66 篇文章
  • 累计创建 0 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Docker

zyixin
2022-04-01 / 0 评论 / 0 点赞 / 2,311 阅读 / 41,366 字
温馨提示:
本文最后更新于 2022-04-01,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

容器化技术Docker

Docker容器基本概念

Docker系统
Docker系统有两个程序:docker服务端和docker客户端
—docker服务端
------是一个服务进程,管理着所有的容器。

—docker客户端
------扮演着docker服务端的远程控制器,可以用来控制docker的服务端进程。

Docker三大核心组件:
—Docker 镜像 - Docker images
—Docker 仓库 - Docker registeries
—Docker 容器 - Docker containers

docker 仓库:
—用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。
—共有的 Docker 仓库名字是Docker Hub。Docker Hub 提供了庞大的镜像集供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建,Docker 仓库是Docker的分发部分。

Docker 镜像:
—Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层组成。Docker使用UnionFS来将这些层联合到单独的镜像中。UnionFS允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖。形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker是如此的轻量。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建,因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发Docker镜像变得简单和快速。
—在Docker的术语里,一个只读层被称为镜像,一个镜像是永久不会变的。
—由于Docker使用一个统一文件系统,Docker进程认为整个文件系统是以读写方式挂载的。但是所有的变更都发生顶层的可写层,而下层的原始的只读镜像文件并未变化。由于镜像不可写,所以镜像是无状态的。
—每一个镜像都可能依赖于由一个或多个下层的组成的另一个镜像。下层那个镜像是上层镜像的父镜像。

镜像名字:
registry/repo:tag
daocloud.io/library/centos:7

基础镜像:
一个没有任何父镜像的镜像,谓之基础镜像。

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

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

Docker安装

自带源安装

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

启动Docker服务
# service docker start
# chkconfig docker on

CentOS 7
# systemctl start docker
# systemctl enable docker

确定docker服务在运行:
结果会显示服务端和客户端的版本,如果只显示客户端版本说明服务没有启动

[root@docker ~]# docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:45:28 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

查看docker基本信息
# docker info

校验Docker的安装

[root@docker ~]# docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
Trying to pull repositery daocloud.io/ubuntu ...
latest: Pulling from daocloud.io/ubuntu
22ecafbbcc4a: Pull complete
580435e0a086: Pull complete
Digest: sha1234jb12kjb3k4kj1h23jkh41kjb23kl14b

如果自动进入下面的容器环境,说明ubuntu镜像运行成功,docker的安装也没有问题,可以操作容器了
root@50a0449d7729:/# pwd
/
root@50a0449d7729:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

docker版本和官方源安装

moby、docker-ce与docker-ee
最早时docker是一个开源项目,主要由docker公司维护。
2017年3月1日起,docker公司将原先的docker项目改名为moby,并创建了docker-ce和docker-ee

三者关系:
–moby是继承了原先的docker的项目,是社区维护的开源项目,谁都可以在moby的基础打造自己的容器产品
–docker-ce是docker公司维护的开源项目,是一个基于moby项目的免费的容器产品
–docker-ee是docker公司维护的闭源产品,是docker公司的商业产品。

–moby project由社区维护,docker-ce project是docker公司维护、docker-ee是闭源的。

要使用免费的docker,从https://github/docker/docker-ce 上获取
要使用收费的docker,从https://www.docker.com/products/docker-enterprise 上获取

docker-ce的发布计划
–v1.13.1之后,发布计划更改为:
–Edge: 月版本,每月发布一次,命名格式为YY.MM,维护到下个月的版本发布
–Stable: 季度发布,每季度发布一次,命名格式为YY.MM,维护4个月

安装:
–Docker-ce的release计划跟随moby的release计划,可以使用下面的命令直接安装最新的docker-ce
# curl -fsSL https://get.docker.com/ | sh

CentOS
如果是centos,上面的安装命令会在系统上添加tum源:/etc/yum.repos.d/docker-ce.repo
# wget https://download.docker.com/linux/centos/docker-ce.repo
# mv docker-ce.repo /etc/yum.repos.d/
# yum install -y docker-ce

或者直接下载rpm安装:
# wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.09..0.ce-1.e17.centos.x86_64.rpm
# yum localinstall docker-ce-17.09…0.ce-1.e17.centos.x86_64.rpm

注意:
–在说docker的时候尽量说Linux docker
–因为Docker on Mac,以及windows Docker (Hyper-V 实现),实际上是基于虚拟化技术实现的,跟我们介绍使用的Linux容器完全不同。

国内源安装新版docker

使用aliyun docker yum源安装新版docker
删除已安装的Docker
	# yum remove docker

配置阿里云Docker yum源
	# yum install -y yum-utils device-mapper-persistent-data lvm2 git
	# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装指定版本
	查看Docker版本
	# yum list docker-ce --showduplicates
	
	安装较旧版本(比如Docker 17.03.2):
		需要指定完整的rpm包的包名,并且加上--setopt=obsoletes=0 参数:
		# yum install -y --setopt=obsoletes=0 docker-ce-17.03.2.ce-1.e17.centos.noarch
		
	安装Docker新版本(比如Docker 19.03.12):
		加上rpm包名的版本号部分或不加都可以:
		# yum install -y docker-ce-19.03.12.ce 
		或者
		# yum install -y docker-ce

启动Docker服务
	# systemctl start docker
	# systemctl enable docker
	
查看docker版本状态:
	# docker -v
	Docker version 19.03.12, build 48a66213fe
	
	# docker version
	Client: Docker Engine - Community
	Version:           19.03.12
 	 API version:       1.40
 	 Go version:        go1.13.10
 	 Git commit:        48a66213fe
 	 Built:             Mon Jun 22 15:46:54 2020
 	 OS/Arch:           linux/amd64
 	 Experimental:      false

	Server: Docker Engine - Community
 	Engine:
  	 Version:          19.03.12
  	 API version:      1.40 (minimum version 1.12)
  	 Go version:       go1.13.10
  	 Git commit:       48a66213fe
  	 Built:            Mon Jun 22 15:45:28 2020
  	 OS/Arch:          linux/amd64
  	 Experimental:     false
 	containerd:
  	 Version:          1.2.13
  	 GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 	runc:
  	 Version:          1.0.0-rc10
  	 GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 	docker-init:
  	 Version:          0.18.0
  	 GitCommit:        fec3683

	# docker info
	Client:
 	 Debug Mode: false

	Server:
 	 Containers: 0
  	  Running: 0
  	  Paused: 0
  	  Stopped: 0
 	 Images: 0
 	 Server Version: 19.03.12
 	 Storage Driver: overlay2
  	  Backing Filesystem: xfs
  	  Supports d_type: true
  	  Native Overlay Diff: true
 	 Logging Driver: json-file
 	 Cgroup Driver: cgroupfs
 	 Plugins:
  	  Volume: local
  	  Network: bridge host ipvlan macvlan null overlay
  	  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 	 Swarm: inactive
 	 Runtimes: runc
 	 Default Runtime: runc
 	 Init Binary: docker-init
 	 containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
 	 runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
 	 init version: fec3683
 Security Options:
  	 seccomp
   	  Profile: default
 	 Kernel Version: 3.10.0-1062.el7.x86_64
 	 Operating System: CentOS Linux 7 (Core)
 	 OSType: linux
 	 Architecture: x86_64
 	 CPUs: 2
 	 Total Memory: 972.4MiB
 	 Name: docker
 	 ID: 6CY2:LNDP:SCOQ:6PXE:F3BE:DLIA:ECFJ:OVTI:RQCR:RX6T:UOC6:UR3B
 	 Docker Root Dir: /var/lib/docker
 	 Debug Mode: false
 	 Registry: https://index.docker.io/v1/
 	 Labels:
 	 Experimental: false
 	 Insecure Registries:
  127.0.0.0/8
 	 Live Restore Enabled: false


[=================================================================]
bonding

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

登入登出docker hub

login		Register or log in to a Docker registry
登陆到自己的Docker register,需由Docker Hub的注册账号
	# docker login
	Username: test
	Password:
	Email: xxx@foxmail.com
	WARNING: login credentials saved in /root/.docker/config.json
	Login Succeeded
	
logout		Log out from a Docker registry
退出登录
	# docker logout
	Remove login credentials for https://index.docker.io/v1/
	
注:推送镜像库到私有源(可注册docker官方账户,推送到官方自有账户)
	

国内镜像源

去查看如何使用aliyun的docker镜像库
去查看如何使用网易蜂巢的docker镜像库

Docker 加速器
使用Docker的时候,需要经常从官方获取镜像,但是由于显而易见的网络原因,拉取镜像的过程非常耗时,严重影响使用Docker的体验,因此DaoCloud推出了加速器工具解决这个难题,通过智能路由和缓存机制,极大提升了国内网络访问Docker Hub的速度,目前已经拥有了广泛的用户群体,并得到了Docker官方的大大推荐。
如果您是在国内的网络环境使用Docker,那么Docker加速器一定能帮助到您。

Docker加速器对Docker的版本有要求吗?
需要Docker 1.8或更高版本才能使用,如果您没有安装Docker或者版本较旧,请安装或升级。

Docker 加速器支持什么系统?
Linux、MacOS以及Windows平台

Docker 加速器是否收费?
DaoCloud为了降低国内用户使用Docker的门槛,提供永久免费的加速器服务,请放心使用。

国内比较好的镜像源:网易蜂巢、aliyun和daocloud,下面是daocloud配置方式:
Docker Hub并没有在国内部署服务器或者使用国内的CDN服务,因此在国内特殊的网络环境下,镜像下载十分耗时。
为了克服跨洋网络延迟,能够快速高效地下载Docker镜像,可以采用DaoCloud提供的服务Docker Hub Mirror,速度快很多
1.注册网站账号
2.然后进入你自己的"控制台",选择"加速器",点"立即开始",接入你自有的主机,就看到如下的内容了
· 下载并安装相关软件
# curl -L -o /tmp/daomonit_amd64.deb https://get.daocloud.io/daomonit/daomonit_amd64.debsudo
# dpkg -4i /tmp/daomonit_amd64.deb
· 配置
# sudo daomonit -token=e16ed1123bjkj123412k23123hjh2314b save-config
· 启动服务
# service daomonit start

3.配置完成后从Docker Hub Mirror下载镜像,命令:
# dao pull ubuntu

注1: 第一次使用daocloud是配置了加速器的,可以直接使用dao pull centos拉取经过加速器之后的镜像,但是后来发现。不使用加速器也可以直接在daocloud官网上找到想要拉取的镜像地址进行拉取,比如: #docker pull
注2: 上面配置加速器的方法,官网会更新,最新方法你应该根据官网提示去操作。
以下为zyixin亲测
使用国内镜像:
进入网站:https://hub.daocloud.io/
注册账号:zyixin
进入镜像市场:填写搜索的镜像名称

选择第一个

点击右边快速部署:

写入名称,选择我的主机,按提示继续在主机上进行所有操作

# mkdir /etc/docker
# cd /etc/docker
# curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
docker version >= 1.12
{“registry-mirrors”: [“http://f1361db2.m.daocloud.io”]}
Success.
You need to restart docker to take effect: sudo systemctl restart docker

出现如下界面:说明自有主机接入完成

接下来我们在镜像市场找到一个centos的镜像:点击右面的拉取按钮,会出现拉取命令如下:
我们按命令执行:

	# docker pull daocloud.io/library/centos:7
	出现如下提示:
	Trying to pull repository daocloud.io/library/centos ...
	latest: Pulling from daocloud.io/library/centos:7
	524b0c1e57f8: Pull complete 
	Digest: sha256:c2f1d5a9c0a81350fa0ad7e1eee99e379d75fe53823d44b5469eb2eb6092c941
	Status: Downloaded newer image for daocloud.io/library/centos:7

查看一下本地镜像:

[root@docker ~]# docker image ls
	REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
	daocloud.io/library/centos   7                   b5b4d78bc90c        2 months ago        203MB

在拉取回来的本地镜像执行命令:

万年不变的"你好世界":
# docker run daocloud.io/library/centos:7 /bin/echo "hello world"
	hello world
	
使用容器中的shell:
[root@docker ~]# docker run -i -t centos /bin/bash
Unable to find image 'centos:latest' locally
Trying to pull repository docker.io/library/centos ...

注意上面这样是不行的,因为默认使用的是docker官方默认镜像库的位置,需要按如下命令执行:
[root@docker ~]# docker run -i -t daocloud.io/library/centos /bin/bash
-i			捕获标准输入输出
-t			分配一个终端或控制台
进去之后可以在里面执行其他命令
[root@f87252fd8a0b /]# ls
anaconda-post.log  bin  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

[root@f87252fd8a0b /]# df -h		#查看镜像本身的磁盘分区
Filesystem               Size  Used Avail Use% Mounted on
overlay                   10G  5.1G  5.0G  51% /
tmpfs                     64M     0   64M   0% /dev
tmpfs                    487M     0  487M   0% /sys/fs/cgroup
shm                       64M     0   64M   0% /dev/shm
/dev/mapper/centos-root   10G  5.1G  5.0G  51% /etc/hosts
tmpfs                    487M     0  487M   0% /proc/asound
tmpfs                    487M     0  487M   0% /proc/acpi
tmpfs                    487M     0  487M   0% /proc/scsi
tmpfs                    487M     0  487M   0% /sys/firmware

镜像管理

搜索镜像:
这种方法只能用于官方镜像库
搜索基于centos操作系统的镜像

# docker search centos

按星级搜索镜像:
查找star数至少为100的镜像,默认不加s选项找出所有相关nginx镜像

# docker search nginx -f stats=100

拉取镜像:

# docker pull centos

查看本地镜像:

# docker image list

查看镜像详情:

# docker image inspect 镜像id

删除镜像:
删除一个或多个,多个之间用空格隔开,可以使用镜像名称或id

# docker rmi daocloud.io/library/centos:7

强制删除: --force
如果镜像正在被使用中,可以使用–force强制删除

# docker rmi daocloud.io/library/centos:7 --force

删除所有镜像:

# docker rmi $(docker images -q)

只查看所有镜像的id:

# docker images -q

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

# docker history daocloud.io/library/centos:7

使用GitHub的软件查看镜像
登陆网址: https://github.com
搜索下载dive,按照文档操作

容器管理

创建新容器但不启动:

# docker create -it daocloud.io/library/centos:7 /bin/bash

创建并运行一个新的Docker容器:

	同一个镜像可以启动多个容器,每次执行run子命令都会运行一个全新的容器
	# docker run -it --restart=always centos /bin/bash
	如果执行成功,说明CentOS容器已经被启动,并且应该已经得到了 bash 提示符.
	-i					#捕获标准输入输出
	-t					#分配一个终端或控制台
	--restart=always	
	#容器随docker engine自启动,因为在重启docker的时候默认容器都会被关闭,也适用于create选项
	--rm
	#默认情况下,每个容器在退出时,它的文件系统都会保存下来,这样一方面调试会方便些,因为你可以通过查看日志等方式来确定最终状态。另一方面,也可以保存容器所产生的数据。
	#但是当你仅仅需要短暂的运行一个容器,并且这些数据不需要保存,你可能就希望Docker能在容器结束时自动清理其所产生的数据,这个时候就需要--rm参数了,注意:--rm 和 -d 不能共用
	
	
	[root@docker setup]# docker ps -a
	CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                      PORTS               NAMES
	60cdb7a5dc67        b5b4d78bc90c                   "/bin/bash"              21 hours ago        Exited (137) 21 hours ago                       jolly_ganguly
	f87252fd8a0b        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Exited (137) 21 hours ago                       busy_newton
	c832ef4c8a67        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Up 21 hours                                     serene_pare
	7a4cdb0922ba        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Exited (0) 21 hours ago                         festive_pare
	d9b498fca7e8        daocloud.io/library/centos:7   "/bin/echo 'hello wo…"   21 hours ago        Exited (0) 21 hours ago                         thirsty_ishizaka
	[root@docker setup]# docker rm 60cdb7a5dc67
	60cdb7a5dc67
	[root@docker setup]# docker ps -a
	CONTAINER ID        IMAGE                          	COMMAND                  CREATED             STATUS                      PORTS               NAMES
	f87252fd8a0b        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Exited (137) 21 hours ago                       busy_newton
	c832ef4c8a67        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Up 21 hours                                     serene_pare
	7a4cdb0922ba        daocloud.io/library/centos:7   "/bin/bash"              21 hours ago        Exited (0) 21 hours ago                         festive_pare
	d9b498fca7e8        daocloud.io/library/centos:7   "/bin/echo 'hello wo…"   21 hours ago        Exited (0) 21 hours ago                         thirsty_ishizaka
	[root@docker setup]# 

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

[root@c832ef4c8a67 /]# exit

如果只想断开和容器的连接而不关闭容器
快捷键: ctrl+p+q

查看容器
只查看运行状态的容器:

# docker ps

查看所有容器 -a参数

# docker ps -a

只查看所有容器id:

# docker ps -a -q

列出最近一次启动的容器:

# docker ps -l

查看容器详细信息

# inspect	Return low-level information on a container or image
用于查看容器的配置信息,包含容器名、环境变量、运行命令、主机配置、网络配置和数据卷配置等

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

提示:
可以使用镜像id的前面部分,不需要完整的id

[root@docker setup]# docker inspect f87
[
    {
        "Id": "f87252fd8a0b25669b65ce4df41b93ca02433428f3588eae67489796ac9c8d56",
        "Created": "2020-07-23T15:34:55.068769829Z",
        "Path": "/bin/bash",
        "Args": [],
        "State": {
            "Status": "exited",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 0,
            "ExitCode": 137,
            "Error": "",
            "StartedAt": "2020-07-23T15:34:55.723390279Z",
            "FinishedAt": "2020-07-23T15:45:26.811653475Z"
        },
     	··· ···

比如:容器里在安装ip或ifconfig命令之前,查看网卡IP显示器IP地址和端口号,如果输出是空的说明没有配置IP地址(不同的Docker容器可以通过此IP地址相互访问)

# # docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器id

# docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' $INSTANCE_ID

# docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' f87252fd8a0b
22/tcp -> 20020

找出特殊的端口映射:
比如找到容器里22端口所映射的docker本机的端口:

# docker inspect --format='{{(index (index .NetworkSettings.Ports "22/tcp") 0).HostPort}}' $INSTANCE_ID
[root@docker setup]# docker inspect --format='{{(index (index .NetworkSettings.Ports "22/tcp") 0).HostPort}}' c832ef4c8a67

--format命令笔记
文档:Docker inspect --format命令笔记.note
链接:http://note.youdao.com/noteshare?id=55f4d6143dabda2035bdf37ff74d2e7e&sub=A4B3693CFA844E8380E80888DE96D2CC

启动容器

# docker start name

关闭容器

# docker stop name
# docker kill name		#强制终止容器

杀死所有running状态的容器

# docker kill $(docker ps -q)

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

删除容器

# docker rm 容器id或名称
要删除一个运行中的容器,添加 -f 参数

根据格式删除所有容器:
# docker rm $(docker ps -qf status=exited)

重启容器

# docker restart name

暂停容器

pause		--暂停容器内的所有进程
	通过docker stats可以观察到此时的资源使用情况是固定不变的,通过docker logs -f也观察不到日志的进一步输出。

恢复容器

unpause		--恢复容器内暂停的进程,与pause参数想对应

# docker start infallible_ramanujan  //这里的名字是状态里面NAMES列出的名字,这种方式同样会让容器运行在后台

设置容器NAME

# docker run -it --name docktest daocloud.io/library/centos:7 /bin/bash
[root@docker setup]# docker ps
CONTAINER ID        IMAGE                          COMMAND             CREATED             STATUS              PORTS               NAMES
03a766e52062        daocloud.io/library/centos:7   "/bin/bash"         42 seconds ago      Up 40 seconds                           docktest
[root@docker setup]# docker stop docktest
docktest
[root@docker setup]# docker ps
CONTAINER ID        IMAGE                          COMMAND             CREATED             STATUS              PORTS               NAMES
6c68e01c9327        daocloud.io/library/centos:7   "/bin/bash"         37 minutes ago      Up 37 minutes                           festive_mahavira

重新进入容器

[root@docker ~]# docker exec -it 8d037a9c25ba /bin/bash
[root@6c68e01c9327 /]#

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

# docker run -d IMAGE[:TAG] 命令
# docker logs container_id		#打印该容器的输出

# docker run -it -d --name mytest docker.io/centos /bin/bash -c "while true; do echo hello world; sleep 2; done"
8d037a9c25ba15f1c7a8b946a2887e9c3e14b56e6a081bb71ef48501b4298fc5

[root@docker ~]# docker logs mytest
hello world
hello world

docker attach container_id #附加该容器的标准输出到当前命令行
[root@docker ~]# docker attach mytest
hello world
hello world
··· ···
此时,ctrl+d等同于exit命令,按ctrl+p+q可以退出到宿主机,而保持container仍然在运行

rename
	Rename a container
	
stats
	Display a live stream or a container(s) resource usage statistics
	--动态显示容器的资源消耗情况,包括:CPU、内存、网络I/O
	
port
	List port mappings or a specific mapping for the CONTAINER
	--输出容器端口与宿主机端口的映射情况
	# dokcer port blog
		80/tcp -> 0.0.0.0:80
	容器blog的内部端口80映射到宿主机的80端口,这样可通过宿主机的80端口查看容器blog提供的服务

连接容器

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

方法2.exec
	通过exec命令可以创建两种任务:后台型任务和交互型任务
	交互型任务:
		# docker exec -it 容器id /bin/bash
		[root@6c68e01c9327 /]# ls
	
	后台型任务:
		# docker exec 容器id touch /testfile

监控容器的运行
可以使用log、top、events、wait这些子命令

	logs:
		使用logs命令查看守护式容器
		可以通过使用docker logs命令来查看容器的运行日志,其中--tail选项可以指定查看最后几条日志,而-t选项则可以对日志条目附加时间戳。使用-f选项可以跟踪日志的输出,直到手动停止。
		# docker logs 容器id/容器名称
		# docker logs -f 容器id/容器名称
		
	top:
		显示一个运行的容器里面的进程信息
		# docker top 容器id/容器名称
		
	events:
		Get real time events from the server
		实时输出Docker服务器端的事件,包括容器的创建,启动,关闭等
		
		# docker start loving_meninsky
		loving_meninsky
		
		# docker events		//不同终端操作
		2020-07-25T18:50:46.849483194+08:00 container create ea2ac321a75ebf8f7ac58e83dfec85c9317e2fe6dad65cdcad88d4f03a268691 (image=daocloud.io/library/centos:7, name=docker3, org.label-schema.build-date=20200504, org.label-schema.license=GPLv2, org.label-schema.name=CentOS Base Image, org.label-schema.schema-version=1.0, org.label-schema.vendor=CentOS, org.opencontainers.image.created=2020-05-04 00:00:00+01:00, org.opencontainers.image.licenses=GPL-2.0-only, org.opencontainers.image.title=CentOS Base Image, org.opencontainers.image.vendor=CentOS)
		2020-07-25T18:50:46.853079529+08:00 container attach ea2ac321a75ebf8f7ac58e83dfec85c9317e2fe6dad65cdcad88d4f03a268691 (image=daocloud.io/library/centos:7, name=docker3, org.label-schema.build-date=20200504, org.label-schema.license=GPLv2, org.label-schema.name=CentOS Base Image, org.label-schema.schema-version=1.0, org.label-schema.vendor=CentOS, org.opencontainers.image.created=2020-05-04 00:00:00+01:00, org.opencontainers.image.licenses=GPL-2.0-only, org.opencontainers.image.title=CentOS Base Image, org.opencontainers.image.vendor=CentOS)

	wait
		Block until a container stops, the print its exit code
		--捕获容器停止时的退出码
		执行此命令后,该命令会"hang"在当前终端,直到容器停止,此时,会打印出容器的退出码
		# docker wait 01d8aa	//不同终端操作
			137
			
	diff
		查看容器内发生改变的文件,以elated_lovelace容器为例
		[root@ea2ac321a75e setup]# touch c.txt
		
		用diff查看:
		包括文件的创建、删除和文件内容的改变都能看到
		[root@docker ~]# docker diff 容器id/名称
		A /home/setup/c.txt
		
		C对应的文件内容的改变,A对应的均是文件或者目录的创建删除
		[root@docker ~]# docker diff docker3 
		C /home
		A /home/setup
		A /home/setup/c.txt

宿主机和容器之间相互COPY文件
cp的用法如下:

Usage: docker cp [OPTIONS] CONTAINER:PATH LOCALPATH
	   docker co [OPTIONS] LOCALPATH CONTAINER:PATH
	   
如:容器mysql中/usr/local/bin/存在docker-entrypoint.sh文件,可如下方式copu到宿主机
# docker cp mysql:/usr/local/bin/docker-entrypoint.sh /root

修改完毕后,将该文件重新copy回容器
# docker cp /root/docker-entrypoint.sh mysql:/usr/local/bin/

容器打包

将容器的文件系统打包成tar文件,也就是把正在运行的容器直接导出为tar包的镜像文件

export
–Export a container’s filesystem as a tar archive

有两种方式(elated_lovelace为容器名):
第一种:

[root@docker ~]# docker export -o elated_lovelace.tar elated_lovelace

第二种:

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

导入镜像归档文件到其他宿主机:
import
–Import the contents from a tarball to create a filesystem image

# docker import elated_lovelace.tar elated_lovelace:v1

镜像迁移

保存一台宿主机上的镜像为tar文件,然后可以导入到其他的宿主机上:

save
	Save an image(s) to a tar archive
	将镜像打包,与下面的load命令相对应
	# docker save -o nginx.tar nginx
	
load
	Load an image from a tar archive or STDIN
	与上面的save命令相对应,将上面save命令打包的镜像通过load命令导入
	# docker load < nginx.tar

容器迁移

# docker export 6c68e01c9327 | gzip > mynginx123.tar
# zcat mynginx123.tar | docker import - mynginx123

Dockerfile构建镜像

通过Dockerfile创建镜像
虽然可以自己操作,rootfs(见’容器文件系统那些事儿’),但Docker提供了一种更便捷的方式,叫作Dockerfile

docker build命令用于根据给定的Dockerfile和上下文以构建Docker镜像

docker build语法:
# docker build [OPTIONS] < PATH | URL | ->

1. 常用选项说明
–build-arg,设置构建时的变量
–no-cache,默认false。设置该选项,将不使用Build Cache构建镜像
–pull,默认false。设置该选项,总是尝试pull镜像的最新版本
–compress,默认false。设置该选项,将使用gzip压缩构建的上下文
–disable-content-truse,默认true。设置该选项,将对镜像进行验证
–file,-f,Dockerfile的完整路径,默认值为’PATH/Dockerfile’
–isolation,默认–isolation=“default”,即Linux命令空间;其他还有process或hyperv
–label,为生成的镜像设置metadata
–squash,默认false。设置该选项,将新构建出的多个层压缩为一个新层,但是将无法在多个镜像之间共享新层;设置该选项,实际上是创建了新image,同时保留原有image
–tag,-t,镜像的名字及tag,通常name:tag或者name格式;可以在一次构建中为一个镜像设置多个tag
–network,默认default。设置该选项,Set the networking mode for the RUN instructions during build
–quiet,-q,默认dalse。设置该选项,Suppress the build output and print image ID on seccess
–force-rm,默认false。设置该选项,总是删除掉中间环节的容器
–rm,默认–rm=true,即整个构建过程成功后删除中间环节的容器

2. PATH | URL | -说明
给出命令执行的上下文
上下文可以是构建执行所在的本地路径,也可以是远程URL,如Git库、tarball或文本文件等
如果是Git库,如https://github.cmo/docker/rootfs.git#container:docker,则隐含先执行git clone --depth 1 --recursive,到本地临时目录;然后再将该临时目录发生给构建进程
构建镜像的进程中,可以通过ADD命令将上下文中的任何文件(注意文件必须在上下文中)加入到镜像中
-表示通过STDIN给出Dockerfile或上下文
示例:

	docker build - <  Dockerfile

说明:该构建过程中只有Dockerfile,没有上下文

	docker build - < context.tar.gz

说明:其中Dockerfile位于context.tar.gz的根路径

	docker build -t champagne/bbauto:latest -t champagne/bbauto:v2.1 .
	docker build -f dockerfile/Dockerfile.debug -t myapp_debug .

2.1 创建镜像所在的文件夹和Dockerfile文件
命令:

	1、mkdir sinatra
	2、cd sinatra
	3、touch Dockerfile

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

	# This is a comment
	FROM daocloud.io/library/ubuntu:14.04
	MAINTAINER tiger tiger@localhost.localdomain
	RUN apt-get update && apt-get install -y ruby ruby-dev
	RUN gem install sinatra

格式说明:
每行命令都是以 INSTRUCTION statement 形式,就是命令 + 清单的模式,命令要大写,"#"是注解。
FROM 命令是告诉docker 我们的镜像是什么
MAINTAINER 是描述镜像的创始人
RUN 命令是在镜像内部执行,就是说他后面的命令应该是针对镜像可以运行的命令

**2.3 创建镜像 **
命令:

	docker build -t tiger/sinatra:v2

docker build 是docker创建镜像的命令
-t 是标识新建的镜像属于ouruser的
sinstra是仓库的名称
:v2 是tag

详细执行过程:

[root@docker nz1902]# docker build -t tiger/nz1902:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM daocloud.io/library/ubuntu:14.04
14.04: Pulling from library/ubuntu
2e6e20c8e2e6: Pull complete 
30bb187ac3fc: Pull complete 
b7a5bcc4a58a: Pull complete 
Digest: sha256:bd0223687054d0f8884fc9e872392c6385a3195d612400495962e270b572ed06
Status: Downloaded newer image for daocloud.io/library/ubuntu:14.04
 ---> 6e4f1fe62ff1
Step 2/4 : MAINTAINER tiger tiger@localhost.localdomain
 ---> [Warning] IPv4 forwarding is disabled. Networking will not work.
 ---> Running in e41339f3bece
Removing intermediate container e41339f3bece
 ---> 2c74b8207eb9
Step 3/4 : RUN apt-get update && apt-get install -y ruby ruby-dev
  ···
Step 4/4 : RUN gem install sinatra
 ---> Running in 262d5f96b949

2.4 创建完成后,从镜像创建容器

# docker run -it tiger/sinatra:v2 /bin/bash

Dockerfile分为四个部分:基础镜像信息、维护者信息、镜像操作指令和容器启动指令。即FROM、MAINTAINER、RUN、CMD四个部分。

常用指令说明

FROM			指定所创建镜像的基础镜像
MAINTAINER		指定维护者信息
RUN				运行命令
CMD				容器启动是默认执行的命令
LABEL			指定生成镜像的元数据标签信息
EXPOSE			声明镜像内服务所监听的端口
ENV				指定环境变量
ADD				复制指定src路径的内容到容器的dest路径下,如果src为tar文件,则自动解压到dest路径下
copy			复制指定src路径的内容到镜像的dest路径下
ENTERPOINT		指定镜像的默认入口
VOLUME			创建数据卷挂载点
USER			指定运行容器时的用户名和UID
WORKDIR			配置工作目录
ARG				指定镜像内使用的参数
ONBUTLD			配置当所创建的镜像作为其他镜像的基础镜像时,所执行创建操作指令
STOPSIGAL		容器退出信号值
HEALTHCHECK		如何进行健康检查
SHELL			指定使用shell的默认shell类型

nginx-dockerfile示例

[root@docker nz1902]# vim Dockerfile
FROM daocloud.io/library/centos:7

ENV TZ=Asia/Shanghai

RUN yum -y install epel-release --nogpgcheck \
        yum -y install gcc openssl openssl-devel pcre-devel zlib-devel

ADD nginx-1.14.0.tar.gz /opt/

WORKDIR /opt/nginx-1.14.0

RUN ./configure --prefix=/opt/nginx --http-log-path=/opt/nginx/logs/access.log --error-log-path=/opt/nginx/logs/error.log --http-client-body-temp-path=/opt/nginx/client/ --http-proxy-temp-path=/opt/nginx/proxy --with-http_stub_status_module --with-file-aio --with-http_flv_module --with-http_gzip_static_module --with-stream --with-threads --user=www --group=www

RUN make && make install
RUN groupadd www && useradd -g www www
WORKDIR /opt/nginx
RUN rm -fr /opt/nginx-1.14.0

ENV NGINX_HOME=/opt/nginx
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/nginx/sbin

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

[root@docker nz1902]# docker build -t tiger/mynginx:v1 .

需要先下载nginx-1.14.0.tar.gz软件包在Dockerfile同级目录下

为解决nginx容器启动时,nginx无法自启动
方案一:
在CMD上添加 RUN nginx
启动命令为 docker run -itd --name container_name image_name:tag, 即可解决该问题
方案二:
替换最后一条
CMD /bin/sh -c ‘“nginx -g daemon off;”’
启动命令为:docker run -itd --name container_name image_name:tag

tomcat-Dockerfile示例

FROM daocloud.io/library/centos:7

ADD jdk-8u171-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-7.0.88.tar.gz /usr/local/

WORKDIR /usr/local/

RUN mv jdk1.8.0_171 jdk && mv apache-tomcat-7.0.88 tomcat

ENV JAVA_HOME=/usr/local/jdk
ENV CLASS_PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
ENV PATH=$JAVA_HOME/bin:$PATH
ENV CATALINA_HOME /usr/local/tomcat

EXPOSE 8080

CMD /usr/local/tomcat/bin/catalina.sh run

需要先下载jdk和tomcat在dockerfile的同级目录下
如果无法自启动,修改CMD为ENTRYPOINT
ENTRYPOINT [“/usr/local/tomcat/bin/catalina.sh”,“run”]

使用.dockerignore文件

可以通过.dockerignore文件(每一行添加一条匹配模式)来让Docker忽略匹配模式路径的目录和文件。例如:

# commit
  */temp*
  */*/tmp*
  tmp?
  ~*

怎么减少镜像的大小?

  1. 减少镜像层级
  2. 及时清理缓存
  3. 不添加多余文件
  4. 尽可能选用较小的基础镜像

Dockerfile网站示例

网址:https://github.com/docker-library/docs

容器与进程

何为容器:
假如要写一个计算加法的小程序,这个程序需要的输入来自于一个文件,计算完成后的结果则输出到另一个文件中。

由于计算机只认0和1,无论用哪种语言编写这段代码,最后都要通过某种方式编译成二进制文件,才能在计算机操作系统中运行起来。

而为了能够让这些代码正常运行,往往还要给它提供数据,比如这个加法程序所需的输入文件。这些数据加上代码本身的二进制文件,放在磁盘上,就是我们平常所说的一个"程序",也叫代码的可执行镜像(executable image)

然后,就可以在计算机上运行这个"程序"了。

首先,操作系统从"程序"中发现输入数据保存在一个文件中,所以这些数据会被加载到内存中待命。同时,操作系统又读取到了计算加法的指令,这时,他就需要指示CPU完成加法操作。而CPU与内存协作进行加法计算,又会使用寄存器存放数值、内存堆栈保存执行的命令和变量。同时,计算机里面还有被打开的文件,以及各种各样的I/O设备在不断地调用中修改自己的状态

就这样,一旦"程序"被执行起来,它就从磁盘上的二进制文件,变成了计算机内存中的数据、寄存器里的值、堆栈中的指令、被打开的文件,以及各种设备的状态信息的一个集合。像这样一个程序运行起来后的计算机执行环境的总和,就是:进程。

所以,对于进程来说,它的静态表现就是程序,平常都安安静静地待在磁盘上;而一旦运行起来,他就变成了计算机里的数据和状态的总和,这就是它的动态表现

容器与进程:
而容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个"边界"。

认识Namespace

Linux 容器中用来实现"隔离"的技术手段:Namespace。Namespace技术实际上修改了应用进程看待整个计算机"视图",即它的"视线"操作系统做了限制,只能"看到"某些指定的内容。但对于宿主机来说,这些被"隔离"了的进程跟其他进程并没有太大区别。

假设已经有一个 Linux 操作系统的Docker项目在运行,环境为:Centos 7和Docker CE 18.05

1.创建一个容器
[root@docker ~]# docker run -it daocloud.io/library/centos:7 /bin/bash
[root@e6f92976da77 /]#

这条指令翻译成人类的语言就是:请帮助我启动一个容器,在容器里执行/bin/bash,并且给我分配一个命令行终端跟这个容器交互。

这样,这台coentos机器就编程了一个宿主机,而一个运行着/bin/bash的容器,就跑在了这个宿主机里面。

2.在容器里执行ps指令,会发现一些有趣的事情:/bin/bash,就是这个容器内部的第1号进程(PID=1),而这个容器里一共只有两个进程在运行。这就意味着,前面执行的/bin/bash,以及我们刚刚执行的ps,已经被Docker隔离在了这一个跟宿主机完全不同的世界当中。

/# ps
PID USER TIME COMMAND
1 root 0:00 /bin/bash
10 root 0:00 ps

本来,每当在宿主机上运行一个/bin/bash程序,操作系统都会给它分配一个进程编号,比如PID=100。这个编号是进程的唯一标识,就像员工的工牌一样,所以PID=100,可以粗略地理解为这个/bin/bash是我们公司里的第100号员工,而第1号员工自然是比尔·盖茨这样统领全局的人物。

而现在,要通过Docker把/bin/bash运行在一个容器当中,这时,Docker就会在这个第100号员工入职时给它施一个"障眼法"让他永远看不到前面的其他99个员工,更看不到1号员工,这样,他就会错误地认为自己就是公司里的第1号员工。

这种机制,其实就是对被隔离应用的进程空间做了手脚,使得这些进程只能看到重新计算过的进程编号,比如 PID=1。可实际上,他们在宿主机的操作系统里,还是原来的第100号进程。

这种技术,就是Linux里面的Namespace机制。

深入理解Namespace机制

Namespace的使用方式:
其实只是Linux创建新进程的一个可选参数。

在Linux系统中创建线程的系统调用是clone(),比如:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
这个系统调用就会为我们创建一个新的进程,并且返回它的进程号pid。

当用clone()系统调用创建一个心得进程时,就可以在参数中指定 CLONE_NEWPID 参数,比如:
int pid = clone(main_function, statk_size, CLONE_NEWPID | SIGCHLD, NULL);
这时,新创建的这个进程将会"看到"一个全新的进程空间,在这空间里,它的PID是1,之所以说"看到",是因为这只是一个"障眼法",在宿主机真实的进程空间里,这个进程的PID还是真实的数值,比如 100。

多次执行上面的clone()调用,就会创建多个PID Namespae,而每个Namespace里的应用进程,都会认为自己是当前容器里的第1号进程,它们即看不到宿主机里真正的进程空间,也看不到其他PID Namespace里的具体情况。

而除了刚用到的PID Namespace,Linux操作系统还提供了Mount、UTS、IPC、Network和User这些Namespace,用来对各种不同的进程上下文进行"障眼法"操作。

比如,Mount Namespace,用来让被隔离进程只看到当前Namespace里的挂载点信息;Network Namespace,用来让被隔离进程看到当前Namespace里的网络设备和配置。

这,就是Linux容器最基本的实现原理了。

所以,Docker容器这个听起来玄而又玄的概念,实际上是在创建容器进程时,指定了这个进程所需要启用的一组Namespace参数。这样,容器就只能"看"到当前Namespace所限定的资源、文件、设备、状态,或者配置。而对于宿主机以及其他不相关的程序,它就看不到了。

所以说,容器,其实是一种特殊的进程而已。

深入理解Cgroup机制

Linux中,Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。

用mount指令把它们展示出来:
[root@docker ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
··· ···
输出结果,是一系列文件系统目录。在/sys/fs/cgroup下面有很多诸如cpuset、cpu、memory这样的子目录。也叫子系统,这些都是这台机器当前可以被Cgroups进行限制的资源种类。而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。

比如,对CPU子系统来说,可以看到如下几个配置文件:

[root@docker ~]# cd /sys/fs/cgroup/
[root@docker cgroup]# ls
blkio cpu cpuacct cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids systemd

比如:cfs_period和cfs_quots 这两个参数需要组合使用,可以用来限制进程在长度为 cfs_period的一段时间内,只能被分配到总量为 cfs_quota的CPU时间。


实验验证:
1、需要在对应的子系统下面创建一个目录,这个目录称为一个"控制组",操作系统会在你新创建的目录下,自动生成该子系统对应的资源限制文件。

[root@docker ~]# cd /sys/fs/cgroup/cpu
[root@docker cpu]# mkdir container
[root@docker cpu]# ls
cgroup.clone_children cgroup.procs container cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares docker release_agent tasks
cgroup.event_control cgroup.sane_behavior cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat notify_on_release system.slice user.slice

2、在后台执行一条脚本

[root@docker cpu]# while : ; do : ; done &
[1] 73898

它执行了一个死循环,可以把计算机的CPU吃到100%,根据它的输出,可以看到这个脚本在后台运行的进程号(PID)是73898

3、用top指令确认一下CPU有没有被打满,用1分看查看cpu
[root@docker cpu]# top
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
在输出里可以看到,CPU的是用率已经100%了。

此时,通过查看container目录下的文件,看到container控制组里的CPU quota还没有任何限制(即:-1),CPU period则是默认的100ms(100000 us);

[root@docker cpu]# cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
-1
[root@docker cpu]# cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
100000

4、通过修改这些文件的内容来设置限制
比如,向container组里的cfs_quota文件写入20ms(20000 us);
[root@docker cpu]# echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us

意味着每100ms的时间里,被该控制组限制的进程只能使用20ms的CPU时间,也就是说这个进程只能使用到20%的cpu带宽。

5、把被限制的进程的PID写入container组里的tasks文件,上面的设置就会对该进程生效:
[root@docker cpu]# echo 73898 > /sys/fs/cgroup/cpu/container/tasks

6、再次用top查看,验证效果:
[root@docker cpu]# top
%Cpu1 : 7.6 us, 0.0 sy, 0.0 ni, 92.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

可以看到,计算机的CPU使用率降到了20%。


除了CPU子系统外,Cgroups的每一项子系统都有其独有的资源限制能力
blkio:
为···块···设···备···设···定···I/O 限···制,一般用于磁盘等设备;
cpuset:
为进程分配单独的CPU核和对应的内存节点;
memory:
为进程设定内存使用的限制。

Linux Cgroups的设计,简单粗暴地理解,就是一个子系统目录加上一组资源限制文件的组合。而对于Docker等Linux容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的PID填写到对应控制组的tasks文件中就可以了。

至于在这些控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定了,比如这样一条命令:
# docker run -it --cpu-period=100000 --cpu-quota=20000 daocloud.io/centos /bin/bash

在启动这个容器后,通过查看 Cgroups 文件系统下,CPU子系统中,"docker"这个控制组里的资源限制文件的内容来确认:
# cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us
100000
# cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us
20000

在centos7里面是下面这个目录:不是上面的docker目录
# cat /sys/fs/cgroup/cpu,cpuacct/system.slice/docker-92f7652e9c0c34e0f4j4bkj5bk3bk5kj849gb445b6i09845m13209.scope/cpu.cfs_quota_us
20000
这就意味着这个Docker容器,只能使用到20%的CPU带宽。

由于一个容器的本质就是一个进程,用户的应用进程实际上就是容器里 PID=1 的进程,也是其他后续创建的所有进程的父进程。这就意味着,在一个容器中,你没办法同时运行两个不同的应用,除非你能事先找到一个公共的 PID=1 的程序来充当两个不同应用的父进程,这也是为什么很多人都会用 system 或者 supervisord 这样的软件来代替应用本事作为容器的启动进程。

这是因为容器本事的设计,就是希望容器和应用能够同生命周期,这个概念对后续的容器编排非常重要。否则,一旦出现类似于"容器是正常运行的,但是里面的应用早已经挂了"的情况,编排系统处理起来就非常麻烦了。

理解容器文件系统

Overlayfs
Overlayfs是一种类似aufs的一种堆叠文件系统,于2014年正式合入Linux-3.18主线内核,目前其功能已经基本稳定(虽然还存在一些特性尚未实现) 且被逐渐推广,特别在容器技术中更是势头难当。
它依赖并建立在其他的文件系统之上(例如ext4fs和xfs等等),并不直接参与磁盘空间结构的划分,仅仅将原来底层文件系统中不同的目录进行"合并",然后向用户呈现,因此对于用户来说,他所见到的overlay文件系统根目录下的内容就来自挂载时所指定的不同目录的"合集"。
Overlayfs基本结构

overlayfs最基本的特性,简单的总结为以下3点:
(1)上下层同名目录合并;
(2)上下层同名文件覆盖;
(3)lower dir文件写时拷贝;
这三点对用户都是不感知的。

lower dirA / lower dirB 目录和upper dir目录

  1. 他们都是来自底层文件系统的不同目录,用户可以自行指定,内部包含了用户想要合并的文件和目录,merge dir目录为挂载点。
  2. 当文件系统挂载后,在merge目录下将会同时看到来自各lower和upper目录下的内容,并且用户也无法(无需)感知这些文件分别那些来自lower dir,哪些来自upper dir,用户看见的只是一个普通的文件系统根目录而已(lower dir可以有多个也可以只有一个)。

upper dir和各lower dir这几个不同的目录并不完全等价,存在层次关系。

  1. 当upper dir和lower dir两个目录存在同名文件时,lower dir的文件将会被隐藏,用户只能看见来自upper dir的文件
  2. lower dir也存在相同的层次关系,较上层屏蔽较下层的同名文件。
  3. 如果存在同名的目录,那就继续合并(lower dir和upper dir合并到挂载点目录其实就是合并一个典型的例子)。

读写数据:

  1. 各层目录中的upper dir是可读写的目录,当用户通过merge dir向其中一个来自upper dir的文件写入数据时,那数据将直接写入upper dir下原来的文件中,删除文件也是用同理;
  2. 而各lower dir则是只读的,在overlayfs挂载后无论如何操作merge目录中对应来自lower dir的文件或目录,lower dir中的内容均不会发生任何的改变。
  3. 当用户想要往来自lower层的文件添加或修改内容时,overlayfs首先会拷贝一份lower dir中的文件副本到upper dir中,后续的写入和修改操作将会在upper dir下的copy-up的副本文件中进行,lower dir原文件被隐藏。

overlayfs特性带来的好处和应用场景
实际的使用中,会存在以下的多用户复用共享文件和目录的场景
见图
Overlayfs基本结构2

复用共享目录文件

在同一个设备上,用户A和用户B有一些共同使用的共享文件(例如运行程序所依赖的动态链接库等),一般是只读的;同时也有自己的私有文件(例如系统配置文件等),往往是需要能够写入修改的;最后即使用户A修改了被共享的文件也不会影响到用户B。

对于以上的需求场景,我们并不希望每个用户都有一份完全一样的文件副本,因为这样不仅带来空间的浪费也会影响性能,因此overlayfs是一个较为完美的解决方案。我们将这些共享的文件和目录所在的目录设定为lower dir (1~n),将用户私有的文件和目录所在的目录设定为upper dir,然后挂载到用户指定的挂载点,这样即能够保证前面列出的3点需求,同时也能够保证用户A和B独有的目录树结构。最后最为关键的是用户A和用户B在各自挂载目录下看见的共享文件其实是同一个文件,这样磁盘空间的节省自是不必说了,还有就是共享同一份cache而减少内存的使用和提高访问性能,因为只要cache不被回收,只需某个用户首次访问时创建cache,后续其他所有用户都可以通过访问cache来提高IO性能。

上面说的这种使用场景在容器技术中应用最为广泛

Overlay和Overlay2
以Docker容器为例来介绍overlay的两种应用方式:Overlay和Overlay2

  1. Docker容器将镜像层(image layer)作为lower dir
  2. 将容器层(container layer)作为upper dir
  3. 最后挂载到容器merge挂载点,即容器的根目录下。

遗憾的是,早期内核中的overlayfs并不支持多lower layer,在Linux-4.0以后的内核版本中才陆续支持完善。而容器中可能存在多层镜像,所以出现了两种overlayfs的挂载方式,早期的overlay不使用多lower layer的方式挂载而overlay2则使用该方式挂载。

  1. Overlay Driver
    Overlay挂载方式如下
    —该图引用自Miklos Szeredi的《overlayfs and containers》2017 Linux内核大会演讲材料
    Overlayfs基本结构3

Overlay2 Driver

Overlay2的挂载方式比Overlay的要简单许多,它基于内核overlayfs的Multiple lower layers特性实现,不在需要硬链接,直接将镜像层的各个目录设置为overlayfs的各个lower layer即可(Overlayfs最多支持500层lower dir),对比Overlay Driver将减少inode的使用。

分别查看镜像的详细信息和运行成容器之后的容器详细信息

lower1:lower2:lower3:
表示不同的lower层目录,不同的目录使用":"分隔,层次关系依次为lower1 > lower2 > lower3
注:多lower层功能支持在Linux-4.0合入,Linux-3.18版本只能指定一个lower dir

容器卷操作

在Docker中,要想实现数据的持久化(所谓Docker的数据持久化即 数据不随着 Container的结束而结束 ),需要将数据从宿主机挂载到容器中。

目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中;

(1)volumes:Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/Volumes目录中;(最常用的方式
(2)bind mounts:意味着可以存储在宿主机系统的任意位置;(比较常用的方式
(3)tmpfs:挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般不会用的方式

新卷只能在容器创建过程当中挂载
# docker run -it --name="voltest" -v /tmp:/test daocloud.io/library/centos:7 /bin/bash

共享其他容器的卷
# docker run -it --volumes-from bc4181 daocloud.io/library/centos:7 /bin/bash

实际应用中可以利用多个-v选项把宿主机上的多个目录同时共享给新建容器
比如:
# docker run -it -v /abc:/abc -v /def:/def lae9

访问容器应用-端口转发

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

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

mysql应用端口转发:
查看本地地址:
# ip a
	ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:24:ef:d3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.160.15/24 brd 192.168.160.255 scope global ens33
       valid_lft forever preferred_lft forever

运行容器:使用-p作端口转发,把本地3307转发到容器的3306,其他参数需要查看发布容器的页面提示
# docker run --name mysql1 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=12 daocloud.io/library/mysql:5.7

查看IP地址:
[root@docker tmp]# docker inspect nginx1 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.3",
                    "IPAMConfig": null,
                    "IPAddress": "172.17.0.3",
					
通过本地IP:192.168.160.15的3307端口访问容器mysql内的数据库,出现如下提示恭喜你
[root@docker ~]# mysql -uroot -p -h 192.168.160.15 -P3307
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 24144
Server version: 5.7.29 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 


-P:
当使用-P标记时,Docker会随机映射一个49000~49900的端口到内部容器开放的网络端口。如下:

[root@docker ~]# docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
tiger/mynginx                v1                  9c18f18948a3        2 days ago          367MB
daocloud.io/library/centos   7                   b5b4d78bc90c        3 months ago        203MB


[root@docker ~]# docker run --name myredis -P -d docker.io/redis
88bf84df36a9777d2a2127036103ab2e88d67b3959885bd3d560efd4062b9478

[root@docker ~]# docker ps
88bf84df36a9	docker.io/redis		"docker-entrypoint.sh"		4 seconds ago	UP 3 seconds	0.0.0.0:32768->6379/tcp		myredis

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

测试看下,登录redis容器,随意写个数据
[root@docker ~]# docker run --rm -it --name myredis2 --link myredis:redisdb docker.io/redis /bin/bash
root@be33d955d6f4:/data# redis-cli -h redisdb -p 6379
redisdb:6379> set tiger 123
OK
redisdb:6379>

在别的机器上通过上面映射的端口32768连接这个容器的redis
# redis-cli -h 192.168.160.15 -p 32768
192.168.160.15:32768> get tiger
"123"

部署Docker web UI应用

下载并运行容器:

# docker pull uifd/ui-for-docker
# 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-web页面

部署私有仓库应用

Docker-私有化仓库

仓库镜像
Docker hub官方已提供容器镜像registry,用于搭建私有仓库

拉取镜像

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

运行容器

[root@docker ~]# docker run --restart=always -d -p 5000:5000 daocloud.io/library/registry

注:如果创建容器不成功,报错防火墙,解决方案如下

	# systemctl stop firewalld
	# yum install iptables*
	# systemctl start iptables
	# iptables -F
	# systemctl restart docker
	
[root@docker ~]# docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                    NAMES
4c1a9e7f73a1        daocloud.io/library/registry   "/entrypoint.sh /etc…"   38 seconds ago      Up 2 seconds        0.0.0.0:5000->5000/tcp   hungry_bouman

连接容器查看端口状态:

[root@docker ~]# docker exec -it if444285deb8 /bin/sh	//这里是sh,不是bash
/ # netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 :::5000                 :::*                    LISTEN      1/registry

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

[root@docker ~]# curl -I 192.168.160.15:5000
HTTP/1.1 200 OK
Cache-Control: no-cache
Date: Fri, 07 Aug 2020 13:27:43 GMT

为了方便,下载1个比较小的镜像,busybox

[root@docker ~]# docker pull busybox

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

[root@docker ~]# docker tag busybox 本机ip:端口/busybox

这里直接从官方拉的镜像,很慢:

[root@docker ~]# docker tag busybox 192.168.160.15:5000/busybox

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

[root@docker ~]# docker tag daocloud.io/library/mysql 192.168.160.15:5000/daocloud.io/library/mysql

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

修改请求方式为http:
默认为https,不改会报以下错误:
Get https://master.up.com:5000/v1/_ping: http: server gave HTTP response to HTTPS
client

# vim /etc/docker/daemon.json

重启docker

[root@docker ~]# systemctl restart docker

上传镜像到私有仓库

[root@docker ~]# docker push 192.168.160.15:5000/busybox

[root@docker ~]# docker push 192.168.160.15:5000/daocloud.io/library/mysql

查看私有仓库里的所有镜像:
—注意我这里是用的ubuntu的例子

# curl 192.168.160.15:5000/v2/_catalog
	{"repositories": ["daocloud.io/ubuntu"]}

或者:

# curl http://192.168.160.15:5000/v2/daocloud.io/ubuntu/tags/list
	{"name": "daocloud.io/ubuntu", "tags": ["v2"]}

部署Centos7容器应用

镜像下载:
# docker pull daocloud.io/library/centos:latest

包管理:
默认情况下,为了减小镜像的尺寸,在构建Centos镜像时用了yum的nodocs选项。如果您安装一个包后发现文件缺失,请在/etc/yum.conf中注释tsflogs=nodocs并重新安装您的包。

systemd整合:
因为systemd要求CAPSYSADMIN权限,从而得到了读取到宿主机cgroup的能力,Centos7中已经用fakesystemd代替了systemd来解决依赖问题。如果仍然希望使用systemd,可用参考下面的dockerfile:

# vim Dockerfile
FROM daocloud.io/library/centos:7
MAINTAINER "tiger" tiger@aliyun.com
ENV container docker
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*; \
rm -f /etc/systemd/system/*.wants/*; \
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*; \
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init" ]

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

# docker build --rm -t local/c7-systemd .

一个包含systemd的应用容器实例

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

# vin Dockerfile
FROM 192.168.160.15:5000/c7-systemd
RUN yum -y install httpd; yum clean all; systemctl enable httpd.service
EXPOSE 80
CMD [ "/usr/sbin/init" ]

构建镜像:

# docker build --rm -t local/c7-systemd-httpd .

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

# docker run --privileged -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 local/c7-systemd-httpd

注意:上面命令不能添加/bin/bash,添加了会导致服务不可用,而且有些服务可能会发现之前提到的权限不够的问题,但是如果不加会运行在前台(没有用-d),可以用ctrl+p+q放到后台去

测试可用:

# elinks --dump http://docker		//下面为apache默认页面
										Testing 123..
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
		<title>Apache HTTP Server Test Page powered by CentOS</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <!-- Bootstrap -->
    <link href="/noindex/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="noindex/css/open-sans.css" type="text/css" />

容器网络

Docker-容器网络

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

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

# vim /etc/sysconfig/docker-network
DOCKER_NETWORK_OPTIONS= --bip=172.18.42.1/16
#reboot

docker19版本修改

[root@docker ~]# cat /etc/docker/daemon.json 
{ "bip": "172.18.0.1/16" }

docker 130上:

# route add -net 172.18.0.0/16 gw 192.168.160.128

docker 128上:

# route add -net 172.17.0.0/16 gw 192.168.160.130

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

容器固定IP

docker安装后,默认会创建三种网络类型,bridge、host和none
显示当前网络:

[root@docker ~]# docker network list
NETWORK ID          NAME                DRIVER              SCOPE
ae90e1033698        bridge              bridge              local
2165e55c0513        host                host                local
95b533b1f7da        none                null                local

bridge:网络桥接
默认情况下启动、创建容器都是用该模式,所以每次docker容器重启时会按照顺序获取对应ip地址,这就导致容器每次重启,ip都发生变化

none:无指定网络
启动容器时,可以通过-network=none,docker容器不会分配局域网ip
host:主机网络
docker容器的网络会附属在主机上,两者是互通的

创建固定ip容器:
1、创建自定义网络类型,并且指定网段

# docker network create --subnet=192.168.0.0/16 staticnet

通过docker network ls可以查看网络类型中多了一个staticnet

2、使用新的网络类型创建并启动容器

# docker run -it --name userserver --net staticnet --ip 192.168.0.2 centos:7 /bin/bash

通过docker inspect可以查看容器ip为192.168.0.2,关闭容器并重启,发现容器ip并未发生改变

BUG整理

基于centos7的docker容器出现的一个bug
centos7下部署的docker容器中启动服务,报错如下:

[root@19302b5d0734 /]# systemctl restart sshd.service
Failed to get D-Bus connection: Operation not permitted

这是centos7容器里面出现的一个bug
即centos7镜像创建的容器里面安装服务后,不能用systemctl/service启动服务,centos6的容器里没有这个坑!
可以通过使用其他的方式或者换用centos6的镜像来避免这个错误.

解决方案如下:
原因是dbus-daemon没能启动,其实systemctl并不是不可以使用,可以将你的CMD设置为/usr/sbin/init即可.
这样就会自动将dbus等服务启动起来。即采用/usr/sbin/init自动启动dbus daemon

即把之前的容器关闭并删除(docker stop container-id),然后重启启动容器,注意:
启动时一定要加上参数–privileged和/sbin/init,如下:

[root@docker ~]# docker run --privileger -it daocloud.io/library/centos:7 /sbin/init

上面的容器启动后,会一直在卡着的状态中,先不用管,打开另一个终端窗口,查看容器这里注意,宿主机可能会出现注销当前登陆账户的情况

[root@docker ~]# docker ps
CONTAINER ID        IMAGE                          COMMAND             CREATED             STATUS              PORTS               NAMES
19302b5d0734        daocloud.io/library/centos:7   "/sbin/init"         15 minutes ago      Up 14 minutes                          nauseous_shirley

然后按照容器的ID进去,这个时候再根据/bin/bash进入容器(前面加exec -it参数),接着重启ssh服务就ok了

[root@docker ~]# docker exec -it 19302b5d0734 /bin/bash
[root@19302b5d0734 ~]# systemctl restart sshd.service

docker: Error response from daemon: Cannot start container
b8ajkj123498skjjruyt78pkhasdj981kjsd83lksbhsad72: [9] System error: SELinux policy denies access..
如上出现上面的报错,这是由于selinux造成的!需要关闭selinux,如下:
[root@docker ~]# setenforce 0
[root@docker ~]# getenforce
Permissive

docker: Error response from daemon: failed to create endpoint mosredis on network bridge:
iptables failed: iptables --wait -t filter -A DOCKER ! -i docker0 -o docker0 -p tcp -d
172.17.0.6 -j ACCEPT: iptables: No chain/target/match by that name.
(exit status 1).

解决办法:
一般来说,重启docker服务,即可解决这个问题

[root@docker ~]# systemctl restart docker
[root@docker ~]# 

------------

如果重启docker服务解决不了,那么如下操作:
[root@docker ~]# pkill docker
[root@docker ~]# iptables -t nat -F
[root@docker ~]# ifconfig docker0 down
[root@docker ~]# brctl delbr docker0
0

评论区