Docker 实践总结(一)

关于版本

版本概述

Docker从1.13版本之后采用时间线的方式作为版本号,分为社区版CE和企业版EE。而在此之前CentOS官方自带的版本是这样的:

社区版是免费提供给个人开发者和小型团体使用的,企业版会提供额外的收费服务,比如经过官方测试认证过的基础设施、容器、插件等。社区版按照stable和edge两种方式发布,每个季度更新stable版本,如17.06,17.09;每个月份更新edge版本,如17.09,17.10。

版本比较

### 防火墙 关闭firewall
1
2
systemctl stop firewalld.service
systemctl disable firewalld.service

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
yum -y install docker-ce
systemctl enable docker

使用普通用户

  • 1、 首先创建docker用户组,如果docker用户组存在可以忽略

    1
    sudo groupadd docker
  • 2、把用户添加进docker组中

    1
    sudo gpasswd -a ${USER} docker
  • 3、重启docker

    1
    sudo service docker restart
  • 4、如果普通用户执行docker命令,如果提示get …… dial unix /var/run/docker.sock权限不够,则修改/var/run/docker.sock权限
    使用root用户执行如下命令,即可

    1
    sudo chmod a+rw /var/run/docker.sock

配置仓库

18.03

1
2
3
vim /etc/systemd/system/multi-user.target.wants/docker.service
#加入
ExecStart=/usr/bin/dockerd -H unix:// --registry-mirror=http://192.168.1.232:5000

19.03

修改daemon.json

1
2
vim /etc/docker/daemon.json
{"insecure-registries":["192.168.1.232:5000"]}

这样就可以使用自己的镜像仓库啦。如果没有配置这些,会提示

1
Get https://192.168.1.232:5000/v1/users/: http: server gave HTTP response to HTTPS client

镜像示例

1
2
3
4
5
6
FROM 10.110.1.18:5000/base/jdk:1.8.0
ENV LC_ALL en_US.UTF-8
WORKDIR /home/joy/src
COPY xxx.war /home/joy/src
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "xxx.war","--server.port=8080","--Djava.security.egd=file:/dev/./urandom"]
1
2
3
4
-Xms2g -Xmx2g -Xmn512m -XX:PermSize=128M -XX:MaxPermSize=128m -XX:SurvivorRatio=6


docker run -d -m 2g -e JAVA_OPTIONS='-Xmx1g -Xms1g -Xmn512m -XX:PermSize=64m -XX:MaxPermSize=256m' 10.110.1.18:5000/base/xxxx:latest /bin/bash
1
2
3
4
5
6
7
8
9
10
11
12
13
-Xms:java Heap初始大小, 默认是物理内存的1/64。
-Xmx:java Heap最大值,不可超过物理内存。
-Xmn:young generation的heap大小,一般设置为Xmx的3、4分之一 。增大年轻代后,将会减小年老代大小,可以根据监控合理设置。
-Xss:每个线程的Stack大小,而最佳值应该是128K,默认值好像是512k。
-XX:PermSize:设定内存的永久保存区初始大小,缺省值为64M。
-XX:MaxPermSize:设定内存的永久保存区最大大小,缺省值为64M。
-XX:SurvivorRatio:Eden区与Survivor区的大小比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
-XX:+UseParallelGC:F年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:+UseParNewGC:设置年轻代为并行收集,JDK5.0以上,JVM会根据系统配置自行设置,所无需再设置此值。
-XX:ParallelGCThreads:并行收集器的线程数,值最好配置与处理器数目相等 同样适用于CMS。
-XX:+UseParallelOldGC:年老代垃圾收集方式为并行收集(Parallel Compacting)。
-XX:MaxGCPauseMillis:每次年轻代垃圾回收的最长时间(最大暂停时间),如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+ScavengeBeforeFullGC:Full GC前调用YGC,默认是true。

镜像库

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
docker search centos                                       # 查找源中镜像
docker pull centos:6 # 从官方下载centos的docker镜像

docker images # 查看docker镜像
docker ps # 查看docker启动的容器
docker ps -a # 查看docker所有容器 包括未启动的
docker rm $(docker ps -a -q) #删除所有停止的容器

docker run -d -t -i centos:6 /bin/bash # 启动docker隔离的容器 -t 让Docker分配一个伪终端,并绑定到容器的标准输入上. -i 则让容器的标准输入保持打开. -d 守护进程
docker attach ID # 进入后台的容器 指定容器ID # util-linux 也可以进入容器
docker logs ID # 获取容器内输出信息

docker stop ID # 停止已启动的容器
docker start ID # 启动已停止的容器
docker restart ID # 重启容器

docker export 7691a814370e > centos_a.tar # 导出容器快照到本地
cat centos_a.tar | docker import - test/centos_a:v1.0 # 从容器快照文件中再导入为镜像

docker save -o centos.6.tar centos:6 # 保存镜像到文件
docker load --input centos.6.tar # 载入镜像文件

docker rm 容器ID # 删除终止状态的容器 加-f强制终止运行中的容器
docker rmi test/centos_a:v1.0 # 移除本地镜像 在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器
docker commit -m 'nginx from centos7' 12bf9d3e4d94 medivh/nginx:v1 提交更改到一个镜像
docker rm `docker ps -a|grep Exited|awk '{print $1}'` # 删除所有退出的容器
brctl show # 查看网桥

docker pull library/nginx

docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx

mkdir testa # 创建静态资源目录
vim testa/a.txt
aaaaaa

vim Dockerfile # 创建 Dockerfile 文件
FROM nginx
COPY testa /usr/share/nginx/html # 拷贝前面创建的静态资源目录

# Dockerfile用来创建一个自定义的image,包含了用户指定的软件依赖等。当前目录下包含Dockerfile,使用命令build来创建新的image,并命名为 some-content-nginx
docker build -t some-content-nginx .

docker run --name some-nginx -d some-content-nginx

docker run --name XXX -d -p 8080:80 XXX-nginx # 暴漏端口的方式,将容器80端口映射到主机8080端口,可以直接访问
docker run --name XXX -d -p 127.0.0.1::80 XXX-nginx #将容器80端口映射到主机某个端口,访问主机该端口
docker run --name XXX -d -P XXX-nginx #将容器80端口映射到主机某个端口,访问主机该端口
docker run -d --name db training/postgres
docker run -d -P --name web --link db:db training/webapp python app.py # 新建立容器,并连接db容器 --link name:alias

# 启动仓库容器
docker run -d -p 5000:5000 -v /data/registry:/tmp/registry registry

# 可以拉取一个比较小的images做测试
docker pull ubuntu

# 更改images的tag
sudo docker tag ubuntu 192.168.11.247:5000/ubuntu

# Push images
sudo docker push 192.168.11.247:5000/ubuntu

# Pull images
sudo docker pull 192.168.11.247:5000/ubuntu

# 通过API查看
curl http://192.168.11.247:5000/v1/search

# 在私有仓库搜索
docker search 192.168.11.247:5000/ubuntu

导入
cd CentOS-6.6_Base
tar -c .|docker import - "centos-6.6/base"

创建一个带ssh的images
docker build -t "CentOS-6.6/sshd" .

启动一个实例:
docker run -d -p 127.0.0.1:1994:22 CentOS-6.6/sshd

导出镜像
docker save IMAGENAME | bzip2 -9 -c>img.tar.bz2

导入镜像(换一台机器)
bzip2 -d -c <img.tar.bz2 | docker load
curl http://localhost:8080/a.txt

配置

进入容器脚本

1
2
3
4
5
6
7
#!/bin/bash
CNAME=$1
CPID=$(docker inspect --format "{{.State.Pid}}" $CNAME)
nsenter --target "$CPID" --mount --uts --ipc --net --pid


go XXX($cname)

使用的时候直接 脚本+容器ID。

文件互传

文件传输

1
2
docker cp foo.txt mycontainer:/foo.txt
docker cp mycontainer:/foo.txt foo.txt

优化

精简Docker镜像尺寸的好处:

  1. 减少构建时间
  2. 减少磁盘使用量
  3. 减少下载时间
  4. 因为包含文件少,攻击面减小,提高了安全性
  5. 提高部署速度

镜像优化

优化基础镜像

推荐使用alpine/busybox,实在不行就只能用CentOS了。

串联 Dockerfile 指令

大家在定义Dockerfile时,如果太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&和/ 来实现),每一个 RUN 要精心设计,确保安装构建最后进行清理,这样才可以降低镜像体积,以及最大化的利用构建缓存。
将多条RUN命令串联起来构建的镜像大小是每条命令分别RUN的三分之一。

使用多阶段构建

Dockerfile中每条指令都会为镜像增加一个镜像层,并且你需要在移动到下一个镜像层之前清理不需要的组件。实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含你的应用程序以及运行它所需的内容。这被称为“建造者模式”。Docker 17.05.0-ce版本以后支持多阶段构建。使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。

下面是一个使用COPY –from 和 FROM … AS … 的Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Compile
FROM golang:1.9.0 AS builder
WORKDIR /go/src/v9.git...com/.../k8s-monitor
COPY . .
WORKDIR /go/src/v9.git...com/.../k8s-monitor
RUN make build
RUN mv k8s-monitor /root

# Package
# Use scratch image
FROM scratch
WORKDIR /root/
COPY --from=builder /root .
EXPOSE 8080
CMD ["/root/k8s-monitor"]

构建镜像,你会发现生成的镜像只有上面COPY 指令指定的内容,镜像大小只有2M。这样在以前使用两个Dockerfile(一个Dockerfile用于开发和一个用于生产的瘦客户端),现在使用多阶段构建就可以搞定。

修改docker0

在此文件中添加如下一行,然后重启服务。

1
2
3
4
5
vim /etc/docker/daemon.json 
{
"bip": "192.168.102.1/24"
}
systemctl restart docker

构建业务服务镜像技巧

  • 不变或者变化很少的体积较大的依赖库和经常修改的自有代码分开;
  • 因为cache缓存在运行Docker build命令的本地机器上,建议固定使用某台机器来进行Docker build,以便利用cache。

其他优化技巧

如果在RUN命令中执行apt、apk或者yum类工具,可以借助这些工具提供的一些小技巧来减少镜像层数量及镜像大小。举几个例子:

(1)在执行apt-get install -y 时增加选项— no-install-recommends,可以不用安装建议性(非必须)的依赖,也可以在执行apk add时添加选项--no-cache 达到同样效果;

(2)执行yum install -y 时候, 可以同时安装多个工具,比如yum install -y gcc gcc-c++ make …。将所有yum install 任务放在一条RUN命令上执行,从而减少镜像层的数量;

(3)组件的安装和清理要串联在一条指令里面,如apk --update add php7 && rm -rf /var/cache/apk/*,因为Dockerfile的每条指令都会产生一个文件层,如果将apk add …rm -rf … 命令分开,清理无法减小apk命令产生的文件层的大小。清理镜像中缓存文件;CentOS等系统使用yum clean all 命令清理。

安全

疑难杂症

  • 1、docker dead 无法删除

    • 1、检查挂载信息
      1
      grep docker /proc/*/mountinfo > x.log
    • 2、 获取进程ID
      1
      grep -nr '/var/lib/docker/overlay2/bd47a216d1bc2e72ba3155169faee246b2352fe919c8fb4708891ad31d1d31c7/diff' x.log | awk -F ':' '{print $2}'|awk -F '/' '{print $3}’
    • 3、杀死所有相关进程
  • 2、故障