951 lines
39 KiB
Markdown
951 lines
39 KiB
Markdown
|
# 03 Docker容器进阶
|
|||
|
|
|||
|
## 一、Docker私有仓库
|
|||
|
|
|||
|
### **1、仓库镜像**
|
|||
|
|
|||
|
Docker hub官方已提供容器镜像registry,用于搭建私有仓库
|
|||
|
|
|||
|
**拉取镜像**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker pull daocloud.io/library/registry:latest
|
|||
|
```
|
|||
|
|
|||
|
### **2、运行容器**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --name "pri_registry" --restart=always -d -p 5000:5000 daocloud.io/library/registry
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
注:如果创建容器不成功,报错防火墙,解决方案如下
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]#systemctl stop firewalld
|
|||
|
[root@qfedu.com ~]#yum install iptables
|
|||
|
[root@qfedu.com ~]#systemctl start iptables
|
|||
|
[root@qfedu.com ~]#iptables -F
|
|||
|
[root@qfedu.com ~]#systemctl restart docker
|
|||
|
```
|
|||
|
|
|||
|
### **3、查看容器**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker ps
|
|||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|||
|
1f444285bed8 daocloud.io/library/registry "/entrypoint.sh /etc/" 23 seconds ago Up 21 seconds 0.0.0.0:5000->5000/tcp elegant_rosalind
|
|||
|
```
|
|||
|
|
|||
|
### **4、连接容器查看端口状态**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker exec -it 1f444285bed8 /bin/sh //这里是sh 不是bash
|
|||
|
/# netstat -lnp //查看5000端口是否开启
|
|||
|
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
|
|||
|
Active UNIX domain sockets (only servers)
|
|||
|
Proto RefCnt Flags Type State I-Node PID/Program name Path
|
|||
|
```
|
|||
|
|
|||
|
在本机查看能否访问该私有仓库,看看状态码是不是200
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# curl -I 127.0.0.1:5000 //参数是大写的i
|
|||
|
HTTP/1.1 200 OK
|
|||
|
```
|
|||
|
|
|||
|
### 5、仓库功能测试
|
|||
|
|
|||
|
```shell
|
|||
|
为了方便,下载1个比较小的镜像,buysbox
|
|||
|
[root@qfedu.com ~]# docker pull busybox
|
|||
|
|
|||
|
上传前必须给镜像打tag 注明ip和端口:
|
|||
|
[root@qfedu.com ~]# docker tag busybox 私有仓库IP:端口/busybox
|
|||
|
|
|||
|
这是直接从官方拉的镜像,很慢:
|
|||
|
[root@qfedu.com ~]# docker tag busybox 192.168.245.136:5000/busybox
|
|||
|
```
|
|||
|
|
|||
|
下面这个Mysql是我测试的第二个镜像,从daocloud拉取的:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker tag daocloud.io/library/mysql 192.168.245.136:5000/daocloud.io/library/mysql
|
|||
|
注:tag后面可以使用镜像名称也可以使用id,我这里使用的镜像名称,如果使用官方的镜像,不需要加前缀,但是daocloud.io的得加前缀
|
|||
|
```
|
|||
|
|
|||
|
修改请求方式为http
|
|||
|
|
|||
|
```shell
|
|||
|
默认为https,不改会报以下错误:
|
|||
|
Get https://master.up.com:5000/v1/_ping: http: server gave HTTP response to HTTPS client
|
|||
|
[root@qfedu.com ~]# vim /etc/docker/daemon.json
|
|||
|
{ "insecure-registries":["192.168.245.136:5000"] }
|
|||
|
|
|||
|
重启docker:
|
|||
|
[root@qfedu.com ~]# systemctl restart docker
|
|||
|
```
|
|||
|
|
|||
|
上传镜像到私有仓库
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker push 192.168.245.136:5000/busybox
|
|||
|
[root@qfedu.com ~]# docker push 192.168.245.136:5000/daocloud.io/library/mysql
|
|||
|
```
|
|||
|
|
|||
|
查看私有仓库里的所有镜像 api 应用程序接口
|
|||
|
|
|||
|
注:我这里是用的是ubuntu的例子
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# curl 192.168.245.130:5000/v2/_catalog
|
|||
|
{"repositories":["daocloud.io/ubuntu"]}
|
|||
|
或者
|
|||
|
[root@qfedu.com ~]# curl http://192.168.245.130:5000/v2/daocloud.io/ubuntu/tags/list
|
|||
|
{"name":"daocloud.io/ubuntu","tags":["v2"]}
|
|||
|
[root@qfedu.com ~]# curl http://192.168.245.130:5000/v2/repo名字/tags/list
|
|||
|
```
|
|||
|
|
|||
|
拉取镜像测试
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker pull 192.168.245.136:5000/busybox
|
|||
|
```
|
|||
|
|
|||
|
### 6、删除registry内的镜像
|
|||
|
|
|||
|
1)exec进入容器更改registry容器内/etc/docker/registry/config.yml文件
|
|||
|
|
|||
|
```yaml
|
|||
|
storage:
|
|||
|
delete:
|
|||
|
enabled: true
|
|||
|
```
|
|||
|
|
|||
|
**重启registry容器**
|
|||
|
|
|||
|
2) 找出你想要的镜像名称的tag
|
|||
|
|
|||
|
```bash
|
|||
|
# 语法
|
|||
|
# curl -I -X GET <protocol>://<registry_host>/v2/<镜像名>/tags/list
|
|||
|
```
|
|||
|
|
|||
|
**操作**:(加粗为需要修改的地方)
|
|||
|
|
|||
|
\# curl http://**192.168.19.150:5000**/v2/**nginx**/tags/list
|
|||
|
|
|||
|
{"name":"nginx","tags":["vvvv","vv"]}
|
|||
|
|
|||
|
3) 拿到digest_hash参数
|
|||
|
|
|||
|
```bash
|
|||
|
# 语法
|
|||
|
# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X GET http://<仓库地址>/v2/<镜像名>/manifests/<tag>
|
|||
|
```
|
|||
|
|
|||
|
**操作**
|
|||
|
|
|||
|
\# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X GET **http://192.168.19.150:5000**/v2/**nginx**/manifests/**vv**
|
|||
|
|
|||
|
HTTP/1.1 200 OK
|
|||
|
Content-Length: 1366
|
|||
|
Content-Type: application/vnd.docker.distribution.manifest.v2+json
|
|||
|
Docker-Content-Digest: **sha256:16eae91e82b4de8fd701b5a4fbfe56059f4d970878a7c42d7fc98c458ef41aa4**
|
|||
|
Docker-Distribution-Api-Version: registry/2.0
|
|||
|
Etag: "sha256:16eae91e82b4de8fd701b5a4fbfe56059f4d970878a7c42d7fc98c458ef41aa4"
|
|||
|
X-Content-Type-Options: nosniff
|
|||
|
Date: Thu, 13 May 2021 03:57:08 GMT
|
|||
|
|
|||
|
4) 复制digest_hash
|
|||
|
|
|||
|
```bash
|
|||
|
Docker-Content-Digest: <digest_hash>
|
|||
|
```
|
|||
|
|
|||
|
Docker-Content-Digest: **sha256:16eae91e82b4de8fd701b5a4fbfe56059f4d970878a7c42d7fc98c458ef41aa4**
|
|||
|
|
|||
|
5) 删除清单
|
|||
|
|
|||
|
```bash
|
|||
|
# 语法
|
|||
|
# curl -I -X DELETE <protocol>://<registry_host>/v2/<repo_name>/manifests/<digest_hash>
|
|||
|
```
|
|||
|
|
|||
|
**操作**
|
|||
|
|
|||
|
\# curl -I -X DELETE http://**192.168.19.150:5000**/v2/**nginx**/manifests/**sha256:16eae91e82b4de8fd701b5a4fbfe56059f4d970878a7c42d7fc98c458ef41aa4**
|
|||
|
|
|||
|
HTTP/1.1 **202** Accepted
|
|||
|
Docker-Distribution-Api-Version: registry/2.0
|
|||
|
X-Content-Type-Options: nosniff
|
|||
|
Date: Thu, 13 May 2021 04:00:50 GMT
|
|||
|
Content-Length: 0
|
|||
|
|
|||
|
**查看结果nginx:vv已经被删除**
|
|||
|
|
|||
|
[root@master ~]# curl http://**192.168.19.150:5000**/v2/**nginx**/tags/list
|
|||
|
|
|||
|
{"name":"nginx","tags":["vvvv"]}
|
|||
|
|
|||
|
## 二、部署Docker-WebUI
|
|||
|
|
|||
|
### 1、下载并运行容器
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]#docker pull uifd/ui-for-docker
|
|||
|
注意:使用ali加速器可以提升获取Docker官方镜像的速度
|
|||
|
[root@qfedu.com ~]#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
|
|||
|
```
|
|||
|
|
|||
|
### 2、浏览器访问测试
|
|||
|
|
|||
|
```http
|
|||
|
http://ip:9000
|
|||
|
```
|
|||
|
|
|||
|
![image-20200311163102429](assets/image-20200311163102429.png)
|
|||
|
|
|||
|
**其他webui:portainer shipyard**
|
|||
|
|
|||
|
## 三、Docker资源限制
|
|||
|
|
|||
|
在使用 Docker 运行容器时,一台主机上可能会运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。
|
|||
|
|
|||
|
CPU 和内存的资源限制已经是比较成熟和易用,能够满足大部分用户的需求。磁盘限制也是不错的,虽然现在无法动态地限制容量,但是限制磁盘读写速度也能应对很多场景。
|
|||
|
|
|||
|
至于网络,Docker 现在并没有给出网络限制的方案,也不会在可见的未来做这件事情,因为目前网络是通过插件来实现的,和容器本身的功能相对独立,不是很容易实现,扩展性也很差。
|
|||
|
|
|||
|
资源限制一方面可以让我们为容器(应用)设置合理的 CPU、内存等资源,方便管理;另外一方面也能有效地预防恶意的攻击和异常,对容器来说是非常重要的功能。如果你需要在生产环境使用容器,请务必要花时间去做这件事情。
|
|||
|
|
|||
|
### 1、系统压力测试
|
|||
|
|
|||
|
Stress是一个linux下的压力测试工具,专门为那些想要测试自己的系统,完全高负荷和监督这些设备运行的用户。
|
|||
|
|
|||
|
```shell
|
|||
|
安装
|
|||
|
[root@qfedu.com ~]# yum install stress -y
|
|||
|
|
|||
|
测试场景举例
|
|||
|
测试CPU负荷
|
|||
|
[root@qfedu.com ~]# stress -c 4 //增加4个cpu进程,处理sqrt()函数函数,以提高系统CPU负荷
|
|||
|
|
|||
|
内存测试
|
|||
|
[root@qfedu.com ~]# stress –i 4 –vm 10 –vm-bytes 1G –vm-hang 100 –timeout 100s
|
|||
|
新增4个io进程,10个内存分配进程,每次分配大小1G,分配后不释放,测试100S
|
|||
|
|
|||
|
磁盘I/O测试
|
|||
|
[root@qfedu.com ~]# stress –d 1 --hdd-bytes 3G //新增1个写进程,每次写3G文件块
|
|||
|
|
|||
|
硬盘测试(不删除)
|
|||
|
[root@qfedu.com ~]# stress –i 1 –d 10 --hdd-bytes 3G –hdd-noclean
|
|||
|
新增1个IO进程,10个写进程,每次写入3G文件块,且不清除,会逐步将硬盘耗尽。
|
|||
|
```
|
|||
|
|
|||
|
**Stress各主用参数说明**
|
|||
|
|
|||
|
-表示后接一个中划线,--表示后接2个中划线,均可用于stress后接参数,不同表达方式
|
|||
|
|
|||
|
> -?
|
|||
|
>
|
|||
|
> --help 显示帮助信息
|
|||
|
>
|
|||
|
> --version 显示软件版本信息
|
|||
|
>
|
|||
|
> -t secs:
|
|||
|
>
|
|||
|
> --timeout secs指定运行多少秒
|
|||
|
>
|
|||
|
> --backoff usecs 等待usecs微秒后才开始运行
|
|||
|
>
|
|||
|
> -c forks:
|
|||
|
>
|
|||
|
> --cpu forks 产生多个处理sqrt()函数的CPU进程
|
|||
|
>
|
|||
|
> -m forks
|
|||
|
>
|
|||
|
> --vm forks:产生多个处理malloc()内存分配函数的进程,后接进程数量
|
|||
|
>
|
|||
|
> -i forks
|
|||
|
>
|
|||
|
> --io forks:产生多个处理sync()函数的磁盘I/O进程
|
|||
|
>
|
|||
|
> --vm-bytes bytes:指定内存的byte数,默认值是1
|
|||
|
>
|
|||
|
> --vm-hang:表示malloc分配的内存多少时间后在free()释放掉
|
|||
|
>
|
|||
|
> -d :
|
|||
|
>
|
|||
|
> --hdd:写进程,写入固定大小,通过mkstemp()函数写入当前目录
|
|||
|
>
|
|||
|
> --hdd-bytes bytes:指定写的byte数,默认1G
|
|||
|
>
|
|||
|
> --hdd-noclean:不要将写入随机ascii数据的文件unlink,则写入的文件不删除,会保留在硬盘空间。
|
|||
|
|
|||
|
### 2、Cpu资源限制
|
|||
|
|
|||
|
Cpu 资源
|
|||
|
|
|||
|
主机上的进程会通过时间分片机制使用 CPU,CPU 的量化单位是频率,也就是每秒钟能执行的运算次数。为容器限制 CPU 资源并不能改变 CPU 的运行频率,而是改变每个容器能使用的 CPU 时间片。理想状态下,CPU 应该一直处于运算状态(并且进程需要的计算量不会超过 CPU 的处理能力)。
|
|||
|
|
|||
|
#### 1、限制Cpu Share
|
|||
|
|
|||
|
什么是cpu share?
|
|||
|
|
|||
|
docker 允许用户为每个容器设置一个数字,代表容器的 CPU share,默认情况下每个容器的 share 是 1024。这个 share 是相对的,本身并不能代表任何确定的意义。当主机上有多个容器运行时,每个容器占用的 CPU 时间比例为它的 share 在总额中的比例。docker 会根据主机上运行的容器和进程动态调整每个容器使用 CPU 的时间比例。
|
|||
|
|
|||
|
例:
|
|||
|
|
|||
|
如果主机上有两个一直使用 CPU 的容器(为了简化理解,不考虑主机上其他进程),其 CPU share 都是 1024,那么两个容器 CPU 使用率都是 50%;如果把其中一个容器的 share 设置为 512,那么两者 CPU 的使用率分别为 67% 和 33%;如果删除 share 为 1024 的容器,剩下来容器的 CPU 使用率将会是 100%。
|
|||
|
|
|||
|
好处:
|
|||
|
|
|||
|
能保证 CPU 尽可能处于运行状态,充分利用 CPU 资源,而且保证所有容器的相对公平;
|
|||
|
|
|||
|
缺点:
|
|||
|
|
|||
|
无法指定容器使用 CPU 的确定值。
|
|||
|
|
|||
|
设置 CPU share 的参数:
|
|||
|
|
|||
|
-c --cpu-shares,它的值是一个整数。
|
|||
|
|
|||
|
我的机器是 4 核 CPU,因此运行一个stress容器,使用 stress 启动 4 个进程来产生计算压力:
|
|||
|
|
|||
|
**注意测试的cpu核数必须和机器的核数一致测试结果才有效**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker pull progrium/stress
|
|||
|
[root@qfedu.com ~]# yum install htop -y
|
|||
|
[root@qfedu.com ~]# docker run --rm -it progrium/stress --cpu 4
|
|||
|
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 12000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 4 [7] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 9000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 3 [8] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 6000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 2 [9] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 1 [10] forked
|
|||
|
```
|
|||
|
|
|||
|
在另外一个 Terminal 使用 htop 查看资源的使用情况:
|
|||
|
|
|||
|
![image-20200311163709474](assets/image-20200311163709474.png)
|
|||
|
|
|||
|
上图中看到,CPU 四个核资源都达到了 100%。四个 stress 进程 CPU 使用率没有达到 100% 是因为系统中还有其他机器在运行。为了比较,另外启动一个 share 为 512 的容器:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it -c 512 progrium/stress --cpu 4
|
|||
|
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 12000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 4 [6] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 9000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 3 [7] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 6000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 2 [8] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 1 [9] forked
|
|||
|
```
|
|||
|
|
|||
|
因为默认情况下,容器的 CPU share 为 1024,所以这两个容器的 CPU 使用率应该大致为 2:1,下面是启动第二个容器之后的监控截图:
|
|||
|
|
|||
|
![image-20200311163727343](assets/image-20200311163727343.png)
|
|||
|
|
|||
|
两个容器分别启动了四个 stress 进程,第一个容器 stress 进程 CPU 使用率都在 54% 左右,第二个容器 stress 进程 CPU 使用率在 25% 左右,比例关系大致为 2:1,符合之前的预期。
|
|||
|
|
|||
|
#### 2、限制Cpu 核数
|
|||
|
|
|||
|
注:可以和cpu绑定一起使用
|
|||
|
|
|||
|
限制容器能使用的 CPU 核数
|
|||
|
|
|||
|
-c --cpu-shares 参数只能限制容器使用 CPU 的比例,或者说优先级,无法确定地限制容器使用 CPU 的具体核数;从 1.13 版本之后,docker 提供了 --cpus 参数可以限定容器能使用的 CPU 核数。这个功能可以让我们更精确地设置容器 CPU 使用量,是一种更容易理解也因此更常用的手段。
|
|||
|
|
|||
|
--cpus 后面跟着一个浮点数,代表容器最多使用的核数,可以精确到小数点二位,也就是说容器最小可以使用 0.01 核 CPU。
|
|||
|
|
|||
|
限制容器只能使用 1.5 核数 CPU:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it --cpus 1.5 progrium/stress --cpu 3
|
|||
|
stress: info: [1] dispatching hogs: 3 cpu, 0 io, 0 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 9000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 3 [7] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 6000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 2 [8] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 1 [9] forked
|
|||
|
```
|
|||
|
|
|||
|
在容器里启动三个 stress 来跑 CPU 压力,如果不加限制,这个容器会导致 CPU 的使用率为 300% 左右(也就是说会占用三个核的计算能力)。实际的监控如下图:
|
|||
|
|
|||
|
![image-20200311163759973](assets/image-20200311163759973.png)
|
|||
|
|
|||
|
可以看到,每个 stress 进程 CPU 使用率大约在 50%,总共的使用率为 150%,符合 1.5 核的设置。
|
|||
|
|
|||
|
如果设置的 --cpus 值大于主机的 CPU 核数,docker 会直接报错:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it --cpus 8 progrium/stress --cpu 3
|
|||
|
docker: Error response from daemon: Range of CPUs is from 0.01 to 4.00, as there are only 4 CPUs available.
|
|||
|
See 'docker run --help'.
|
|||
|
```
|
|||
|
|
|||
|
如果多个容器都设置了 --cpus ,并且它们之和超过主机的 CPU 核数,并不会导致容器失败或者退出,这些容器之间会竞争使用 CPU,具体分配的 CPU 数量取决于主机运行情况和容器的 CPU share 值。也就是说 --cpus 只能保证在 CPU 资源充足的情况下容器最多能使用的 CPU 数,docker 并不能保证在任何情况下容器都能使用这么多的 CPU(因为这根本是不可能的)。
|
|||
|
|
|||
|
#### 3、CPU 绑定
|
|||
|
|
|||
|
限制容器运行在某些 CPU 核
|
|||
|
|
|||
|
注:
|
|||
|
|
|||
|
一般并不推荐在生产中这样使用
|
|||
|
|
|||
|
docker 允许调度的时候限定容器运行在哪个 CPU 上。
|
|||
|
|
|||
|
限制容器运行在哪些核上并不是一个很好的做法,因为它需要实现知道主机上有多少 CPU 核,而且非常不灵活。除非有特别的需求,一般并不推荐在生产中这样使用。
|
|||
|
|
|||
|
假如主机上有 4 个核,可以通过 --cpuset 参数让容器只运行在前两个核上:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it --cpuset-cpus=0,1 progrium/stress --cpu 2
|
|||
|
stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 6000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 2 [7] forked
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogcpu worker 1 [8] forked
|
|||
|
```
|
|||
|
|
|||
|
这样,监控中可以看到只有前面两个核 CPU 达到了 100% 使用率。
|
|||
|
|
|||
|
![image-20200311163900910](assets/image-20200311163900910.png)
|
|||
|
|
|||
|
--cpuset-cpus 参数可以和 -c --cpu-shares 一起使用,限制容器只能运行在某些 CPU 核上,并且配置了使用率。
|
|||
|
|
|||
|
### 3、Mem资源限制
|
|||
|
|
|||
|
Docker 默认没有对容器内存进行限制,容器可以使用主机提供的所有内存。
|
|||
|
|
|||
|
不限制内存带来的问题:
|
|||
|
|
|||
|
这是非常危险的事情,如果某个容器运行了恶意的内存消耗软件,或者代码有内存泄露,很可能会导致主机内存耗尽,因此导致服务不可用。可以为每个容器设置内存使用的上限,一旦超过这个上限,容器会被杀死,而不是耗尽主机的内存。
|
|||
|
|
|||
|
限制内存带来的问题:
|
|||
|
|
|||
|
限制内存上限虽然能保护主机,但是也可能会伤害到容器里的服务。如果为服务设置的内存上限太小,会导致服务还在正常工作的时候就被 OOM 杀死;如果设置的过大,会因为调度器算法浪费内存。
|
|||
|
|
|||
|
合理做法:
|
|||
|
|
|||
|
1. 为应用做内存压力测试,理解正常业务需求下使用的内存情况,然后才能进入生产环境使用
|
|||
|
|
|||
|
2. 一定要限制容器的内存使用上限,尽量保证主机的资源充足,一旦通过监控发现资源不足,就进行扩容或者对容器进行迁移如果可以(内存资源充足的情况)
|
|||
|
|
|||
|
3. 尽量不要使用 swap,swap 的使用会导致内存计算复杂,对调度器非常不友好
|
|||
|
|
|||
|
Docker 限制容器内存使用量:
|
|||
|
|
|||
|
docker 启动参数中,和内存限制有关的包括(参数的值一般是内存大小,也就是一个正数,后面跟着内存单位 b、k、m、g,分别对应 bytes、KB、MB、和 GB):
|
|||
|
|
|||
|
> -m --memory:
|
|||
|
>
|
|||
|
> 容器能使用的最大内存大小,最小值为 4m
|
|||
|
>
|
|||
|
> --memory-swap:
|
|||
|
>
|
|||
|
> 容器能够使用的 swap 大小
|
|||
|
>
|
|||
|
> --memory-swap 必须在 --memory 也配置的情况下才能有用。
|
|||
|
>
|
|||
|
> 如果 --memory-swap 的值大于 --memory,那么容器能使用的总内存(内存 + swap)为 --memory-swap 的值,能使用的 swap 值为 --memory-swap 减去 --memory 的值
|
|||
|
>
|
|||
|
> 如果 --memory-swap 为 0,或者和 --memory 的值相同,那么容器能使用两倍于内存的 swap 大小,如果 --memory 对应的值是 200M,那么容器可以使用 400M swap
|
|||
|
>
|
|||
|
> 如果 --memory-swap 的值为 -1,那么不限制 swap 的使用,也就是说主机有多少 swap,容器都可以使用
|
|||
|
>
|
|||
|
> --memory-swappiness:
|
|||
|
>
|
|||
|
> 默认情况下,主机可以把容器使用的匿名页(anonymous page)swap 出来,你可以设置一个 0-100 之间的值,代表允许 swap 出来的比例
|
|||
|
>
|
|||
|
> --memory-reservation:
|
|||
|
>
|
|||
|
> 设置一个内存使用的 soft limit,如果 docker 发现主机内存不足,会执行 OOM 操作。这个值必须小于 --memory 设置的值
|
|||
|
>
|
|||
|
> --kernel-memory:
|
|||
|
>
|
|||
|
> 容器能够使用的 kernel memory 大小,最小值为 4m。
|
|||
|
>
|
|||
|
> --oom-kill-disable:
|
|||
|
>
|
|||
|
> 是否运行 OOM 的时候杀死容器。只有设置了 -m,才可以把这个选项设置为 false,否则容器会耗尽主机内存,而且导致主机应用被杀死
|
|||
|
|
|||
|
如果限制容器的内存使用为 64M,在申请 64M 资源的情况下,容器运行正常(如果主机上内存非常紧张,并不一定能保证这一点):
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 64M --vm-hang 0
|
|||
|
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
|
|||
|
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogvm worker 1 [7] forked
|
|||
|
stress: dbug: [7] allocating 67108864 bytes ...
|
|||
|
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
|
|||
|
stress: dbug: [7] sleeping forever with allocated memory
|
|||
|
.....
|
|||
|
```
|
|||
|
|
|||
|
而如果申请 100M 内存,会发现容器里的进程被 kill 掉了(worker 7 got signal 9,signal 9 就是 kill 信号)
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 100M --vm-hang 0
|
|||
|
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
|
|||
|
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
|
|||
|
stress: dbug: [1] using backoff sleep of 3000us
|
|||
|
stress: dbug: [1] --> hogvm worker 1 [7] forked
|
|||
|
stress: dbug: [7] allocating 104857600 bytes ...
|
|||
|
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
|
|||
|
stress: FAIL: [1] (415) <-- worker 7 got signal 9
|
|||
|
stress: WARN: [1] (417) now reaping child worker processes
|
|||
|
stress: FAIL: [1] (421) kill error: No such process
|
|||
|
stress: FAIL: [1] (451) failed run completed in 0s
|
|||
|
```
|
|||
|
|
|||
|
### 4、IO 资源限制【扩展】
|
|||
|
|
|||
|
对于磁盘来说,考量的参数是容量和读写速度,因此对容器的磁盘限制也应该从这两个维度出发。目前 docker 支持对磁盘的读写速度进行限制,但是并没有方法能限制容器能使用的磁盘容量(一旦磁盘 mount 到容器里,容器就能够使用磁盘的所有容量)。
|
|||
|
|
|||
|
限制磁盘的读写速率
|
|||
|
|
|||
|
docker 允许你直接限制磁盘的读写速率,对应的参数有:
|
|||
|
|
|||
|
--device-read-bps:磁盘每秒最多可以读多少比特(bytes)
|
|||
|
|
|||
|
--device-write-bps:磁盘每秒最多可以写多少比特(bytes)
|
|||
|
|
|||
|
上面两个参数的值都是磁盘以及对应的速率,限制 limit 为正整数,单位可以是 kb、mb 和 gb。
|
|||
|
|
|||
|
比如可以把设备的读速率限制在 1mb:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run -it --device /dev/sda:/dev/sda --device-read-bps /dev/sda:1mb ubuntu:16.04 bash
|
|||
|
root@6c048edef769# cat /sys/fs/cgroup/blkio/blkio.throttle.read_bps_device
|
|||
|
8:0 1048576
|
|||
|
root@6c048edef769# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=5M count=10
|
|||
|
10+0 records in
|
|||
|
10+0 records out
|
|||
|
52428800 bytes (52 MB) copied, 50.0154 s, 1.0 MB/s
|
|||
|
```
|
|||
|
|
|||
|
从磁盘中读取 50m 花费了 50s 左右,说明磁盘速率限制起了作用。
|
|||
|
|
|||
|
另外两个参数可以限制磁盘读写频率(每秒能执行多少次读写操作):
|
|||
|
|
|||
|
> --device-read-iops:磁盘每秒最多可以执行多少 IO 读操作
|
|||
|
>
|
|||
|
> --device-write-iops:磁盘每秒最多可以执行多少 IO 写操作
|
|||
|
|
|||
|
上面两个参数的值都是磁盘以及对应的 IO 上限。
|
|||
|
|
|||
|
比如,可以让磁盘每秒最多读 100 次:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run -it --device /dev/sda:/dev/sda --device-read-iops /dev/sda:100 ubuntu:16.04 bash root@2e3026e9ccd2:/[root@qfedu.com ~]# dd iflag=direct,nonblock if=/dev/sda of=/dev/null bs=1k count=1000
|
|||
|
1000+0 records in
|
|||
|
1000+0 records out
|
|||
|
1024000 bytes (1.0 MB) copied, 9.9159 s, 103 kB/s
|
|||
|
```
|
|||
|
|
|||
|
从测试中可以看出,容器设置了读操作的 iops 为 100,在容器内部从 block 中读取 1m 数据(每次 1k,一共要读 1000 次),共计耗时约 10s,换算起来就是 100 iops/s,符合预期结果。
|
|||
|
|
|||
|
## 四、端口转发
|
|||
|
|
|||
|
使用端口转发解决容器端口访问问题
|
|||
|
|
|||
|
-p 手工指定
|
|||
|
|
|||
|
-P 随机端口
|
|||
|
|
|||
|
### **1、MySQL应用端口转发**
|
|||
|
|
|||
|
-p
|
|||
|
|
|||
|
> 创建应用容器的时候,一般会做端口映射,这样是为了让外部能够访问这些容器里的应用。可以用多个-p指定多个端口映射关系。
|
|||
|
|
|||
|
本例使用-p把本地3307转发到容器的3306,其他参数需要查看发布容器的页面提示
|
|||
|
|
|||
|
```shell
|
|||
|
宿主机ip为:192.168.245.134
|
|||
|
|
|||
|
运行容器:
|
|||
|
[root@qfedu.com ~]# docker run --name mysql1 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123 daocloud.io/library/mysql
|
|||
|
|
|||
|
查看Ip地址:
|
|||
|
[root@qfedu.com ~]# docker inspect mysql1 | grep IPAddress
|
|||
|
"IPAddress": "172.17.0.2"
|
|||
|
|
|||
|
通过本地IP:192.168.245.134的3307端口访问容器mysql1内的数据库,出现如下提示恭喜你
|
|||
|
[root@qfedu.com ~]# mysql -u root -p123 -h 192.168.245.134 -P3307
|
|||
|
Welcome to the MariaDB monitor. Commands end with ; or \g.
|
|||
|
Your MySQL connection id is 3
|
|||
|
Server version: 5.7.18 MySQL Community Server (GPL)
|
|||
|
Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.
|
|||
|
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
|
|||
|
MySQL [(none)]>
|
|||
|
```
|
|||
|
|
|||
|
### **2、Redis应用端口转发**
|
|||
|
|
|||
|
-P
|
|||
|
|
|||
|
> Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。如下:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker images
|
|||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
|||
|
docker.io/redis latest e4a35914679d 2 weeks ago 182.9 MB
|
|||
|
|
|||
|
[root@qfedu.com ~]# docker run --name myredis -P -d docker.io/redis
|
|||
|
805d0e21e531885aad61d3e82395210b50621f1991ec4b7f9a0e25c815cc0272
|
|||
|
|
|||
|
[root@qfedu.com ~]# docker ps
|
|||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|||
|
805d0e21e531 docker.io/redis "docker-entrypoint.sh" 4seconds ago Up 3 seconds 0.0.0.0:32768->6379/tcp myredis
|
|||
|
从上面的结果中可以看出,本地主机的32768端口被映射到了redis容器的6379端口上,也就是说访问本机的32768
|
|||
|
端口即可访问容器内redis端口。
|
|||
|
|
|||
|
另外运行一个redis容器并且使用--link连接到上面的myredis,使用内置redis-cli连接myredis容器写入数据
|
|||
|
--link myredis:redisdb
|
|||
|
myredis为被连接的容器
|
|||
|
redisdb是被连接容器的别名
|
|||
|
使用--link好处是可以不用通过容器的ip地址连接容器,可以直接使用被连接容器的名称或者别名
|
|||
|
[root@qfedu.com ~]# docker run --rm -it --name myredis2 --link myredis:redisdb docker.io/redis /bin/bash
|
|||
|
root@5e67cc8c7e9f:/data# redis-cli -h redisdb -p 6379
|
|||
|
redisdb:6379> set wing 123
|
|||
|
OK
|
|||
|
redisdb:6379> get wing
|
|||
|
"123"
|
|||
|
|
|||
|
|
|||
|
在别的机器上通过上面映射的端口32768连接这个容器的redis
|
|||
|
[root@qfedu.com ~]# redis-cli -h 192.168.245.134 -p 32768
|
|||
|
192.168.1.23:32768> get wing
|
|||
|
"123"
|
|||
|
```
|
|||
|
|
|||
|
## 五、容器卷
|
|||
|
|
|||
|
### 1、容器卷操作
|
|||
|
|
|||
|
容器卷是容器和宿主机之间的文件共享方式之一
|
|||
|
|
|||
|
```shell
|
|||
|
新卷只能在容器创建过程当中挂载
|
|||
|
[root@qfedu.com ~]# docker run -it --name="voltest" -v /tmp:/test daocloud.io/library/centos:5 /bin/bash
|
|||
|
|
|||
|
共享其他容器的卷:
|
|||
|
[root@qfedu.com ~]# docker run -it --volumes-from bc4181 daocloud.io/library/centos:5 /bin/bash
|
|||
|
|
|||
|
实际应用中可以利用多个-v选项把宿主机上的多个目录同时共享给新建容器:
|
|||
|
|
|||
|
比如:
|
|||
|
[root@qfedu.com ~]# docker run -it -v /abc:/abc -v /def:/def 1ae9
|
|||
|
[root@qfedu.com ~]# docker run -v /vol/index.html:/usr/share/nginx/html/index.html -it nginx /bin/bash
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
|
|||
|
注意:
|
|||
|
|
|||
|
如果是文件共享,数据不能同步更新
|
|||
|
|
|||
|
### 2、Volume【扩展阅读】
|
|||
|
|
|||
|
容器技术使用了 rootfs 机制和 Mount Namespace,构建出了一个同宿主机完全隔离开的文件系统环境。这时候,就需要考虑这样两个问题:
|
|||
|
|
|||
|
容器里进程新建的文件,怎么才能让宿主机获取到?
|
|||
|
|
|||
|
宿主机上的文件和目录,怎么才能让容器里的进程访问到?
|
|||
|
|
|||
|
这正是 Docker Volume 要解决的问题:Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。
|
|||
|
|
|||
|
在 Docker 项目里,它支持两种 Volume 声明方式,可以把宿主机目录挂载进容器的 /test 目录当中:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run -v /test ...
|
|||
|
[root@qfedu.com ~]# docker run -v /home:/test ...
|
|||
|
```
|
|||
|
|
|||
|
这两种声明方式的本质是相同的:都是把一个宿主机的目录挂载进了容器的 /test 目录。
|
|||
|
|
|||
|
第一种情况没有显示声明宿主机目录,Docker 就会默认在宿主机上创建一个临时目录 /var/lib/docker/volumes/[VOLUME_ID]/_data,然后把它挂载到容器的 /test 目录上。
|
|||
|
|
|||
|
第二种情况,Docker 就直接把宿主机的 /home 目录挂载到容器的 /test 目录上。
|
|||
|
|
|||
|
那么,Docker 又是如何做到把一个宿主机上的目录或者文件,挂载到容器里面去呢?难道又是 Mount Namespace 的黑科技吗?
|
|||
|
|
|||
|
实际上,并不需要这么麻烦。
|
|||
|
|
|||
|
已经介绍过,当容器进程被创建之后,尽管开启了 Mount Namespace,但是在它执行 chroot(或者 pivot_root)之前,容器进程一直可以看到宿主机上的整个文件系统。
|
|||
|
|
|||
|
而宿主机上的文件系统,也自然包括了要使用的容器镜像。这个镜像的各个层,保存在 /var/lib/docker/aufs/diff 目录下,在容器进程启动后,它们会被联合挂载在 /var/lib/docker/aufs/mnt/ 目录中,这样容器所需的 rootfs 就准备好了。
|
|||
|
|
|||
|
所以,只需要在 rootfs 准备好之后,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。
|
|||
|
|
|||
|
由于执行这个挂载操作时,"容器进程"已经创建了,也就意味着此时 Mount Namespace 已经开启了。所以,这个挂载事件只在这个容器里可见。你在宿主机上,是看不见容器内部的这个挂载点的。这就保证了容器的隔离性不会被 Volume 打破。
|
|||
|
|
|||
|
注意:这里提到的 " 容器进程 ",是 Docker 创建的一个容器初始化进程 (dockerinit),而不是应用进程 (ENTRYPOINT + CMD)。dockerinit 会负责完成根目录的准备、挂载设备和目录、配置 hostname 等一系列需要在容器内进行的初始化操作。最后,它通过 execv() 系统调用,让应用进程取代自己,成为容器里的 PID=1 的进程。
|
|||
|
|
|||
|
而这里要使用到的挂载技术,就是 Linux 的绑定挂载(bind mount)机制。它的主要作用就是,允许你将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上。并且,这时你在该挂载点上进行的任何操作,只是发生在被挂载的目录或者文件上,而原挂载点的内容则会被隐藏起来且不受影响。
|
|||
|
|
|||
|
其实,如果你了解 Linux 内核的话,就会明白,绑定挂载实际上是一个 inode 替换的过程。在 Linux 操作系统中,inode 可以理解为存放文件内容的"对象",而 dentry,也叫目录项,就是访问这个 inode 所使用的"指针"
|
|||
|
|
|||
|
mount --bind /home /test,会将 /home 挂载到 /test 上。其实相当于将 /test 的 dentry,重定向到了 /home 的 inode。这样当修改 /test 目录时,实际修改的是 /home 目录的 inode。这也就是为何,一旦执行 umount 命令,/test 目录原先的内容就会恢复:因为修改真正发生在的,是 /home 目录里。
|
|||
|
|
|||
|
进程在容器里对这个 /test 目录进行的所有操作,都实际发生在宿主机的对应目录(比如,/home,或者 /var/lib/docker/volumes/[VOLUME_ID]/_data)里,而不会影响容器镜像的内容。
|
|||
|
|
|||
|
这个 /test 目录里的内容,既然挂载在容器 rootfs 的可读写层,它会不会被 docker commit 提交掉呢?
|
|||
|
|
|||
|
也不会。
|
|||
|
|
|||
|
原因前面提到过。容器的镜像操作,比如 docker commit,都是发生在宿主机空间的。而由于 Mount Namespace 的隔离作用,宿主机并不知道这个绑定挂载的存在。所以,在宿主机看来,容器中可读写层的 /test 目录(/var/lib/docker/aufs/mnt/[可读写层 ID]/test),始终是空的。
|
|||
|
|
|||
|
不过,由于 Docker 一开始还是要创建 /test 这个目录作为挂载点,所以执行了 docker commit 之后,新产生的镜像里,会多出来一个空的 /test 目录。毕竟,新建目录操作,又不是挂载操作,Mount Namespace 对它可起不到"障眼法"的作用。
|
|||
|
|
|||
|
```shell
|
|||
|
1.启动一个 helloworld 容器,给它声明一个 Volume,挂载在容器里的 /test 目录上:
|
|||
|
[root@qfedu.com ~]# docker run -d -v /test helloworld
|
|||
|
cf53b766fa6f
|
|||
|
|
|||
|
2.容器启动之后,查看一下这个 Volume 的 ID:
|
|||
|
[root@qfedu.com ~]# docker volume ls
|
|||
|
DRIVER VOLUME NAME
|
|||
|
local cb1c2f7221fa9b0971cc35f68aa1034824755ac44a034c0c0a1dd318838d3a6d
|
|||
|
|
|||
|
3.使用这个 ID,可以找到它在 Docker 工作目录下的 volumes 路径:
|
|||
|
[root@qfedu.com ~]# ls /var/lib/docker/volumes/cb1c2f7221fa/_data/
|
|||
|
这个 _data 文件夹,就是这个容器的 Volume 在宿主机上对应的临时目录了。
|
|||
|
|
|||
|
4.在容器的 Volume 里,添加一个文件 text.txt:
|
|||
|
[root@qfedu.com ~]# docker exec -it cf53b766fa6f /bin/sh
|
|||
|
cd test/
|
|||
|
touch text.txt
|
|||
|
|
|||
|
5.再回到宿主机,就会发现 text.txt 已经出现在了宿主机上对应的临时目录里:
|
|||
|
[root@qfedu.com ~]# ls /var/lib/docker/volumes/cb1c2f7221fa/_data/
|
|||
|
text.txt
|
|||
|
可是,如果你在宿主机上查看该容器的可读写层,虽然可以看到这个 /test 目录,但其内容是空的:
|
|||
|
[root@qfedu.com ~]# ls /var/lib/docker/aufs/mnt/6780d0778b8a/test
|
|||
|
```
|
|||
|
|
|||
|
可以确认,容器 Volume 里的信息,并不会被 docker commit 提交掉;但这个挂载点目录 /test 本身,则会出现在新的镜像当中。以上内容,就是 Docker Volume 的核心原理了。
|
|||
|
|
|||
|
Docker 容器"全景图":
|
|||
|
|
|||
|
![image-20200311164951880](assets/image-20200311164951880.png)
|
|||
|
|
|||
|
一个"容器",实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。
|
|||
|
|
|||
|
## 六、优化Centos7镜像
|
|||
|
|
|||
|
镜像下载:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker pull daocloud.io/library/centos:latest
|
|||
|
```
|
|||
|
|
|||
|
包管理:
|
|||
|
|
|||
|
默认情况下,为了减小镜像的尺寸,在构建 CentOS 镜像时用了yum的nodocs选项。 如果您安装一个包后发现文件缺失,请在/etc/yum.conf中注释掉tsflogs=nodocs并重新安装您的包。
|
|||
|
|
|||
|
systemd 整合:
|
|||
|
|
|||
|
因为 systemd 要求 CAPSYSADMIN 权限,从而得到了读取到宿主机 cgroup 的能力,CentOS7 中已经用 fakesystemd 代替了 systemd 来解决依赖问题。 如果仍然希望使用 systemd,可用参考下面的 Dockerfile:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# vim Dockerfile
|
|||
|
FROM daocloud.io/library/centos:7
|
|||
|
MAINTAINER "wing" wing@qq.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。然后再构建基础镜像:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker build --rm -t local/c7-systemd .
|
|||
|
```
|
|||
|
|
|||
|
一个包含 systemd 的应用容器示例
|
|||
|
|
|||
|
为了使用像上面那样包含 systemd 的容器,需要创建一个类似下面的Dockerfile:
|
|||
|
|
|||
|
```dockerfile
|
|||
|
[root@qfedu.com ~]# vim Dockerfile
|
|||
|
FROM local/c7-systemd
|
|||
|
RUN yum -y install httpd; yum clean all; systemctl enable httpd.service
|
|||
|
EXPOSE 80
|
|||
|
CMD ["/usr/sbin/init"]
|
|||
|
```
|
|||
|
|
|||
|
构建镜像:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker build --rm -t local/c7-systemd-httpd .
|
|||
|
```
|
|||
|
|
|||
|
运行包含 systemd 的应用容器:
|
|||
|
|
|||
|
为了运行一个包含 systemd 的容器,需要使用--privileged选项, 并且挂载主机的 cgroups 文件夹。 下面是运行包含 systemd 的 httpd 容器的示例命令:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# 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放到后台去
|
|||
|
|
|||
|
测试可用:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# elinks --dump http://docker //下面为apache默认页面
|
|||
|
Testing 123..
|
|||
|
This page is used to test the proper operation of the [1]Apache HTTP
|
|||
|
server after it has been installed. If you can read this page it means
|
|||
|
that this site is working properly. This server is powered by [2]CentOS.
|
|||
|
```
|
|||
|
|
|||
|
再来个安装openssh-server的例子:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# vim Dockerfile
|
|||
|
FROM local/c7-systemd
|
|||
|
RUN yum -y install openssh-server; yum clean all; systemctl enable sshd.service
|
|||
|
RUN echo 1 | passwd --stdin root
|
|||
|
EXPOSE 22
|
|||
|
CMD ["/usr/sbin/init"]
|
|||
|
|
|||
|
[root@qfedu.com ~]# docker build --rm -t local/c7-systemd-sshd .
|
|||
|
[root@qfedu.com ~]# docker run --privileged -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 2222:22 local/c7-systemd-sshd
|
|||
|
[root@qfedu.com ~]# ssh docker -p 2222 //docker为我宿主机的主机名称
|
|||
|
```
|
|||
|
|
|||
|
## 七、Docker网络
|
|||
|
|
|||
|
### 容器网络
|
|||
|
|
|||
|
注:面试用,用了编排之后就没有用了
|
|||
|
|
|||
|
Docker安装后,默认会创建三种网络类型,bridge、host和none
|
|||
|
|
|||
|
查看当前网络:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker network list
|
|||
|
NETWORK ID NAME DRIVER SCOPE
|
|||
|
90b22f633d2f bridge bridge local
|
|||
|
e0b365da7fd2 host host local
|
|||
|
da7b7a090837 none null local
|
|||
|
```
|
|||
|
|
|||
|
1、bridge:网络桥接
|
|||
|
|
|||
|
默认情况下启动、创建容器都是用该模式,所以每次docker容器重启时会按照顺序获取对应ip地址,这就导致容器每次重启,ip都发生变化
|
|||
|
|
|||
|
2、none:无指定网络
|
|||
|
|
|||
|
启动容器时,可以通过--network=none,docker容器不会分配局域网ip
|
|||
|
|
|||
|
3、host:主机网络
|
|||
|
|
|||
|
docker容器的网络会附属在主机上,两者是互通的。
|
|||
|
|
|||
|
使用host网络创建容器:
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run -it --name testnginx1 --net host 27a18801 /bin/bash
|
|||
|
```
|
|||
|
|
|||
|
4、固定ip(个人认为是bridge):
|
|||
|
|
|||
|
**创建固定Ip的容器**
|
|||
|
|
|||
|
4.1、创建自定义网络类型,并且指定网段
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]#docker network create --subnet=192.168.0.0/16 staticnet
|
|||
|
```
|
|||
|
|
|||
|
通过docker network ls可以查看到网络类型中多了一个staticnet
|
|||
|
|
|||
|
4.2、使用新的网络类型创建并启动容器
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]#docker run -it --name userserver --net staticnet --ip 192.168.0.2 centos:6 /bin/bash
|
|||
|
```
|
|||
|
|
|||
|
通过docker inspect可以查看容器ip为192.168.0.2,关闭容器并重启,发现容器ip并未发生改变
|
|||
|
|
|||
|
不能使用默认桥接网络,不然会报错
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker run -it --rm --name "test" -v /test/b.txt:/tmp/b.txt -v /tmp:/abc --net bridge --ip 172.17.0.9 1f8fe54 /bin/sh
|
|||
|
docker: Error response from daemon: user specified IP address is supported on user defined networks only.
|
|||
|
```
|
|||
|
|
|||
|
### 扩展:异主容器互联
|
|||
|
|
|||
|
路由方式
|
|||
|
|
|||
|
小规模docker环境大部分运行在单台主机上,如果公司大规模采用docker,那么多个宿主机上的docker如何互联
|
|||
|
|
|||
|
![image-20200311220604137](assets/image-20200311220604137.png)
|
|||
|
|
|||
|
Docker默认的内部ip为172.17.42.0网段,所以必须要修改其中一台的默认网段以免ip冲突。
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# vim /etc/sysconfig/docker-network
|
|||
|
DOCKER_NETWORK_OPTIONS= --bip=172.18.42.1/16
|
|||
|
[root@qfedu.com ~]# reboot
|
|||
|
```
|
|||
|
|
|||
|
```shell
|
|||
|
docker 130上:
|
|||
|
[root@qfedu.com ~]# route add -net 172.18.0.0/16 gw 192.168.18.128
|
|||
|
docker 128上:
|
|||
|
[root@qfedu.com ~]# route add -net 172.17.0.0/16 gw 192.168.18.130
|
|||
|
```
|
|||
|
|
|||
|
现在两台宿主机里的容器就可以通信了。
|
|||
|
|
|||
|
## 八、Docker数据存储位置
|
|||
|
|
|||
|
**查看默认存储位置**
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# docker info | grep Root
|
|||
|
Docker Root Dir: /var/lib/docker
|
|||
|
```
|
|||
|
|
|||
|
**修改默认存储位置**
|
|||
|
|
|||
|
在dockerd的启动命令后面追加--data-root参数指定新的位置
|
|||
|
|
|||
|
```shell
|
|||
|
[root@qfedu.com ~]# vim /usr/lib/systemd/system/docker.service
|
|||
|
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root=/data
|
|||
|
[root@qfedu.com ~]# systemctl daemon-reload
|
|||
|
[root@qfedu.com ~]# systemctl restart docker
|
|||
|
查看是否生效:
|
|||
|
[root@qfedu.com ~]# docker info | grep Root
|
|||
|
Docker Root Dir: /data
|
|||
|
```
|