###1.Docker介绍###
什么是Docker
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。Docker为应用打包、部署平台,而非单纯的虚拟化技术。
一个完整的 Docker 由以下几个部分组成:
DockerClient 客户端
Docker Daemon 守护进程
Docker Image 镜像
DockerContainer 容器
VM VS Container
传统虚拟化与容器技术对比
docker容器的优势
更高效的利用系统资源
更快速的启动时间
一致的运行环境
持续支付和部署
更轻松的迁移
更轻松的维护和拓展• 对于开发人员:Build once、Run anywhere。
• 对于运维人员:Configure once、Run anything。
容器技术大大提升了IT人员的幸福指数!
容器工作原理
Docker 使用客户端 - 服务器(C/S)架构,使用远程API管理和创建Docker 容器。Docker 客户端与 Docker 守护进程通信,后者负责构建,运行和分发 Docker容器。Docker客户端和守护进程可以在同一系统上运行,也可以将Docker客户端连接到远程Docker守护进程。Docker客户端和守护进程使用REST API,通过UNIX套接字或网络接口进行通信。
Client:客户端通过命令行或其他工具与守护进程通信,客户端会将这些命令发送给守护进程,然后执行这些命令。命令使用Docker API,Docker客户端可以与多个守护进程通信。
Docker daemon:Docker守护进程(docker daemon)监听Docker API请求并管理Docker对象,如镜像,容器,网络和卷。守护程序还可以与其他守护程序通信以管理Docker服务。
Docker Host:Docker Host 是物理机或虚拟机,用于执行Docker守护进程的仓库。
Docker Registry:Docker仓库用于存储Docker镜像,可以是Docker Hub这种公共仓库,也可以是个人搭建的私有仓库。使用docker pull或docker run命令时,将从配置的仓库中提取所需的镜像。使用docker push命令时,镜像将被推送到配置的仓库。
###2.Docker部署###
[root@server1 ~]# cd /etc/yum.repos.d/
[root@server1 yum.repos.d]# vim docker.repo
[docker]
name=docker-ce
baseurl=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/7/x86_64/stable/
gpgcheck=0[update]
name=centos
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/7/extras/x86_64/
gpgcheck=0安装docker-ce
[root@server1 yum.repos.d]# yum install -y docker-ce启动服务
[root@server1 ~]# systemctl enable --now docker[root@server1 ~]# docker info
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled ##若出现这两个警告需要激活内核选项激活内核选项
[root@server1 ~]# vim /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1[root@server1 ~]# sysctl --system
[root@server1 ~]# systemctl restart docker
启动容器
[root@server1 ~]# docker run -d --name web1 -p 80:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
3f9582a2cbe7: Pull complete
9a8c6f286718: Pull complete
e81b85700bc2: Pull complete
73ae4d451120: Pull complete
6058e3569a68: Pull complete
3a1b8f201356: Pull complete
Digest: sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2
Status: Downloaded newer image for nginx:latest
0ab8d0e38142e97b8c611b156d723b2546fc1623909b65a0f5f016d8a2a1668d显示激活的容器
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ab8d0e38142 nginx "/docker-entrypoint.…" 8 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web1访问网页:http://192.168.56.11/
删除容器
[root@server1 ~]# docker rm -f web1查询镜像
[root@server1 ~]# docker search yakexi007拉取镜像到本地
[root@server1 ~]# docker pull yakexi007/game2048启动容器
[root@server1 ~]# docker run -d --name web1 -p 80:80 yakexi007/game2048访问网页:http://192.168.56.11/
###3.Docker镜像###
(1)镜像的分层结构
• 共享宿主机的kernel
• base镜像提供的是最小的Linux发行版
• 同一docker主机支持运行多种Linux发行版
• 采用分层结构的最大好处是:共享资源
• Copy-on-Write 可写容器层
• 容器层以下所有镜像层都是只读的
• docker从上往下依次查找文件
• 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
• 一个镜像最多127层
(2)镜像常用子命令
• images 显示镜像列表
• history 显示镜像构建历史
• commit 从容器创建镜像
• build 从Dockerfile构建镜像
• tag 给镜像打标签
• search 搜索镜像
• pull 从仓库拉取镜像
• push 上传镜像到仓库
• rmi 删除镜像
(3)镜像构建
①docker commit
docker commit 构建新镜像三部曲• 运行容器• 修改容器• 将容器保存为新的镜像缺点:• 效率低、可重复性弱、容易出错• 使用者无法对镜像进行审计,存在安全隐患
[root@server1 ~]# docker pull busybox ##拉取一个小的镜像用来做测试
Using default tag: latest
latest: Pulling from library/busybox
ec562eabd705: Pull complete
Digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest[root@server1 ~]# docker run -it --name demo busybox ##交互式进入容器
/ # touch file1
/ # touch file2
ctrl+d: 退出容器后容器自动关闭
ctrl+pq: 退出容器后继续在后台运行显示所有容器,包括被停掉的
[root@server1 ~]# docker ps -a启动容器
[root@server1 ~]# docker start demo进入容器
[root@server1 ~]# docker attach demo提交容器变更到新的镜像
[root@server1 ~]# docker commit -m "add files" demo demo:v1[root@server1 ~]# docker history demo:v1
IMAGE CREATED CREATED BY SIZE COMMENT
2b25ea847e4e 9 seconds ago sh 39B add files
827365c7baf1 13 days ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 13 days ago /bin/sh -c #(nop) ADD file:3f2f2548e5ddec788… 4.86MB这种方式不利于安全审计删除镜像
[root@server1 docker]# docker rmi demo:v1
②Dockerfile
dockerfile常用指令
• FROM
指定base镜像,如果本地不存在会从远程仓库下载。
• MAINTAINER
设置镜像的作者,比如用户邮箱等。
• COPY
把文件从build context复制到镜像
支持两种形式:COPY src dest 和 COPY ["src", "dest"]
src必须指定build context中的文件或目录
• ADD
用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest, 也可以自动下载URL并拷贝到镜像:
ADD html.tar /var/www
ADD http://ip/html.tar /var/www
• ENV
设置环境变量,变量可以被后续的指令使用:
ENV HOSTNAME sevrer1.example.com
• EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去:
EXPOSE 80
• VOLUME
申明数据卷,通常指定的是应用的数据挂在点:
VOLUME ["/var/www/html"]
• WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当 前工作目录,如果目录不存在会自动创建。
• RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim
• CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。
• Shell和exec格式的区别
# cat Dockerfile
FROM busybox
ENV name world
ENTRYPOINT echo "hello, $name"
Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而下面的 exec格式不会:
# cat Dockerfile
FROM busybox
ENV name world
ENTRYPOINT ["/bin/echo", "hello, $name"]
需要改写成以下形式:
# cat Dockerfile
FROM busybox
ENV name world
ENTRYPOINT ["/bin/sh", "-c", "echo hello, $name"]
Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额 外参数可以在容器启动时动态替换。在shell格式时ENTRYPOINT会忽 略任何CMD或docker run提供的参数。
# cat Dockerfile
FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]
看下在运行容器时的区别:
# docker run --rm busybox:v1
hello world
# docker run --rm busybox:v1 linux
hello linux
官方推荐使用exec格式书写
一个简单dockerfile
配置nginx服务dockerfile
[root@server1 ~]# mdkir docker
[root@server1 ~]# cd docker/
[root@server1 docker]# cp ~/nginx-1.23.3.tar.gz .
[root@server1 docker]# vim Dockerfile
FROM centos:7
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --with-http_ssl_module --with-http_stub_status_module
RUN make
RUN make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]构建镜像
[root@server1 docker]# docker build -t webserver:v1 .[root@server1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v1 bfd6774cc216 8 seconds ago 494MB[root@server1 docker]# docker history webserver:v1清理所有停止的容器
[root@server1 docker]# docker container prune启动容器
[root@server1 docker]# docker run -d --name web1 webserver:v1
查看容器详情
[root@server1 docker]# docker inspect web1
访问容器
[root@server1 docker]# curl 172.17.0.2
###4.Docker镜像优化###
• 选择最精简的基础镜像• 减少镜像的层数• 清理镜像构建的中间产物• 注意优化网络请求• 尽量去用构建缓存• 使用多阶段构建镜像
(1)缩短镜像层
[root@server1 docker]# vim Dockerfile
FROM centos:7 as build
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx-1.23.3 && yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"][root@server1 docker]# docker build -t webserver:v2 .[root@server1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v2 caf0f80f2332 4 seconds ago 317MB
webserver v1 bfd6774cc216 About an hour ago 494MB
(2)多阶段构建
[root@server1 docker]# vim Dockerfile
FROM centos:7 as build
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx-1.23.3 && yum clean allFROM centos:7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"][root@server1 docker]# docker build -t webserver:v3 .[root@server1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v3 1ac964f2cefe 29 seconds ago 205MB
webserver v2 caf0f80f2332 3 minutes ago 317MB
webserver v1 bfd6774cc216 About an hour ago 494MB
(3)使用最精简的基础镜像
导入基础镜像
[root@server1 ~]# docker load -i base-debian12.tar[root@server1 ~]# mkdir new
[root@server1 ~]# cd new/
[root@server1 new]# vim Dockerfile
FROM nginx:latest as base# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONERUN mkdir -p /opt/var/cache/nginx && \cp -a --parents /usr/lib/nginx /opt && \cp -a --parents /usr/share/nginx /opt && \cp -a --parents /var/log/nginx /opt && \cp -aL --parents /var/run /opt && \cp -a --parents /etc/nginx /opt && \cp -a --parents /etc/passwd /opt && \cp -a --parents /etc/group /opt && \cp -a --parents /usr/sbin/nginx /opt && \cp -a --parents /usr/sbin/nginx-debug /opt && \cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \cp -a --parents /usr/lib/x86_64-linux-gnu/libpcre* /opt && \cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtimeFROM gcr.io/distroless/base-debian11COPY --from=base /opt /EXPOSE 80 443ENTRYPOINT ["nginx", "-g", "daemon off;"][root@server1 new]# docker build -t webserver:v4 .[root@server1 new]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v4 c0c4e1d49f3d 4 seconds ago 34MB
webserver v3 1ac964f2cefe 12 minutes ago 205MB
webserver v2 caf0f80f2332 15 minutes ago 317MB
webserver v1 bfd6774cc216 About an hour ago 494MB[root@server1 new]# docker run -d --name web1 webserver:v4[root@server1 new]# curl 172.17.0.2
###5.Docker仓库###
• Docker 仓库是用来包含镜像的位置, Docker 提供一个注册服务器 (Register )来保存多个仓库,每个仓库又可以包含多个具备不同 tag 的镜像。• Docker 运行中使用的默认仓库是 Docker Hub 公共仓库。
(1)Docker Hub
首先注册账号 : https://hub.docker.com/
创建仓库
客户端登录docker hub
[root@server1 ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: yakexi007 #docker hub注册的用户
Password: **** #密码
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded上传镜像
[root@server1 ~]# docker tag webserver:v4 yakexi007/webserver:v4[root@server1 ~]# docker push yakexi007/webserver:v4
The push refers to repository [docker.io/yakexi007/webserver]
bbed3d4e37e0: Pushed
0b3d0512394d: Pushed
5b1fa8e3e100: Pushed
v4: digest: sha256:013969b9116df5dc77ddaef6c500129439e9db94f46b61b148b43e53900623e5 size: 949
(2)配置镜像加速器
从 docker hub 上下载镜像的速度太慢,需要配置镜像加速器,这里以阿里云为例
[root@server1 ~]# vim /etc/docker/daemon.json
{"registry-mirrors": ["https://registry.docker-cn.com"] ##docker国内镜像源
}[root@server1 ~]# systemctl restart docker[root@server1 ~]# docker info
(3)registry私有仓库
docker hub虽然方便,但是还是有限制
• 需要internet连接,速度慢
• 所有人都可以访问
• 由于安全原因企业不允许将镜像放到外网
好消息是docker公司已经将registry开源,我们可以快速构建企业私有仓库
官网:https://docs.docker.com/registry/deploying/
拉取registry镜像
[root@server1 ~]# docker pull registry
运行registry仓库
[root@server1 docker]# docker run -d -p 5000:5000 --restart=always --name registry registry上传镜像
[root@server1 ~]# docker tag nginx:latest localhost:5000/nginx:latest
[root@server1 ~]# docker push localhost:5000/nginx[root@server1 ~]# curl localhost:5000/v2/_catalog
{"repositories":["nginx"]}下载镜像
[root@server1 ~]# docker pull localhost:5000/nginx
(4)insecure registry配置非加密端口
本地搭建好私有仓库后,要验证其他主机是否能远程下载仓库里的镜像
默认情况下用的是https,不能直接下载
需要配置非加密端口强制让其他主机使用非加密的仓库
先给远程主机server2配置docker-ce服务
强制让远程主机访问非加密的私有仓库
配置使用非加密端口
[root@server2 ~]# vim /etc/docker/daemon.json
{"insecure-registries" : ["http://192.168.56.11:5000"]
}[root@server2 docker]# systemctl restart docker拉取镜像
[root@server2 docker]# docker pull 192.168.56.11:5000/nginx
(5)仓库加密
加密部署
升级软件包
[root@server1 ~]# yum install -y openssl11-1.1.1k-4.el7.x86_64.rpm openssl11-libs-1.1.1k-4.el7.x86_64.rpm添加域名解析
[root@server1 ~]# vim /etc/hosts
...
192.168.56.11 server1 reg.westos.org[root@server1 ~]# mkdir certs
[root@server1 ~]# openssl11 req -newkey rsa:4096 -nodes -sha256 -keyout certs/westos.org.key -addext "subjectAltName = DNS:reg.westos.org" -x509 -days 365 -out certs/westos.org.crt启动容器
[root@server1 ~]# docker run -d --restart=always --name registry
-p 443:443 ##-p做端口映射
-v /opt/registry:/var/lib/registry
-v /root/certs:/certs ##-v把冒号前的目录挂载在容器内的冒号后的目录
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 ##registry的http监听443端口
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/westos.org.crt ##证书的访问位置
-e REGISTRY_HTTP_TLS_KEY=/certs/westos.org.key ##key的访问位置
registry ##镜像部署客户端证书,不然会报错
[root@server1 ~]# docker tag nginx:latest reg.westos.org/nginx:latest
[root@server1 ~]# docker push reg.westos.org/nginx:latest The push refers to repository [reg.westos.org/nginx]
Get "https://reg.westos.org/v2/": x509: certificate signed by unknown authority[root@server1 ~]# mkdir -p /etc/docker/certs.d/reg.westos.org/
[root@server1 ~]# cp /root/certs/westos.org.crt /etc/docker/certs.d/reg.westos.org/ca.crt验证[root@server1 ~]# docker push reg.westos.org/nginx:latest[root@server1 reg.westos.org]# curl -k https://reg.westos.org/v2/_catalog
{"repositories":["nginx"]}
远程主机使用测试
server2上移除非安全仓库设置
[root@server2 docker]# mv daemon.json /mnt/
[root@server2 docker]# systemctl restart docker添加解析
[root@server2 ~]# vim /etc/hosts
...
192.168.56.11 server1 reg.westos.org拷贝证书
[root@server1 ~]# cd /etc/docker/
[root@server1 ~]# scp -r certs.d/ server2:/etc/docker/远程拉取server1仓库中镜像
[root@server2 ~]# docker pull reg.westos.org/nginx
Using default tag: latest
latest: Pulling from nginx
Digest: sha256:c94f3436f3bfcb467e9723bdb4957e2e86c00cc5f21e38a40d668c1a4c324696
Status: Downloaded newer image for reg.westos.org/nginx:latest
reg.westos.org/nginx:latest
(6) 仓库认证
[root@server1 ~]# yum install -y httpd-tools
[root@server1 ~]# mkdir auth
[root@server1 ~]# htpasswd -Bc auth/htpasswd admin
New password:
Re-type new password:
Adding password for user admin第二次添加不要加“-c”选项,不然会覆盖
[root@server1 ~]# htpasswd -B auth/htpasswd wxh
New password:
Re-type new password:
Adding password for user wxh[root@server1 ~]# cat auth/htpasswd
admin:$2y$05$Wm2LHttPY5a6i2KMG0fShe92d/PjnaBbGitiClcE3wqHmwO8dIDFm
wxh:$2y$05$9rE9CXyZ1fdcMammhh7f6.soDHgKdSsi0DXBgkRW5sKRw5sEJo1lK删除registry,重建
[root@server1 ~]# docker rm -f registry
[root@server1 ~]# docker run -d -p 443:443 --restart=always --name registry -v /opt/registry:/var/lib/registry -v /root/certs:/certs -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/westos.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/westos.org.key -v /root/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd registry登录仓库,不然无法上传下载
[root@server1 ~]# docker login reg.westos.org
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded
[root@server1 ~]# cat .docker/config.json
{"auths": {"reg.westos.org": {"auth": "YWRtaW46d2VzdG9z"}}
}验证
[root@server1 ~]# docker tag busybox:latest reg.westos.org/busybox:latest
[root@server1 ~]# docker push reg.westos.org/busybox:latest[root@server1 ~]# curl -k https://reg.westos.org/v2/_catalog -u admin:westos
{"repositories":["busybox","nginx"]}登出
[root@server1 ~]# docker logout reg.westos.org
远程主机下载镜像
首先登录仓库
[root@server2 ~]# docker login reg.westos.org
Username: lfj
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded
拉取
[root@server2 ~]# docker pull reg.westos.org/busybox
Using default tag: latest
latest: Pulling from busybox
d0f42ecf7e6c: Pull complete
Digest: sha256:28e01ab32c9dbcbaae96cf0d5b472f22e231d9e603811857b295e61197e40a9b
Status: Downloaded newer image for reg.westos.org/busybox:latest
reg.westos.org/busybox:latest
(7)harbor企业级私有仓库
①仓库部署
删除之前部署的registry,不然会冲突
[root@server1 ~]# docker rm -f registry[root@server1 ~]# tar zxf harbor-offline-installer-v2.5.0.tgz
[root@server1 ~]# cd harbor/
[root@server1 harbor]# cp harbor.yml.tmpl harbor.yml
[root@server1 harbor]# vim harbor.yml
hostname: reg.westos.orghttp:# port for http, default is 80. If https enabled, this port will redirect to https portport: 80https:# https port for harbor, default is 443port: 443# The path of cert and key files for nginxcertificate: /data/certs/westos.org.crtprivate_key: /data/certs/westos.org.key...
harbor_admin_password: westos拷贝证书
[root@server1 ~ ]# mkdir /data
[root@server1 ~ ]# cp -r certs /data部署docker-compose
[root@server1 ~]# mv docker-compose-linux-x86_64-v2.5.0 /usr/local/bin/docker-compose
[root@server1 ~]# chmod +x /usr/local/bin/docker-compose部署harbor
[root@server1 harbor]# ./install.sh
使用浏览器登录仓库 用户名:admin 密码是上面配置文件设置的westoshttps://192.168.76.11
②仓库管理
上传镜像
上传镜像,首先需要执行docker login reg.westos.org,此处已经执行过[root@server1 ~]# docker tag busybox:latest reg.westos.org/library/busybox:latest
[root@server1 ~]# docker tag nginx:latest reg.westos.org/library/nginx:latest[root@server1 ~]# docker push reg.westos.org/library/nginx:latest
[root@server1 ~]# docker push reg.westos.org/library/busybox:latest
拉取镜像
远程主机拉取镜像时,需要先配置默认仓库
配置默认仓库
[root@server2 ~]# vim /etc/docker/daemon.json
{"registry-mirrors": ["https://reg.westos.org"]
}[root@server2 ~]# systemctl restart dockerlibrary项目中的镜像下载时可以直接写镜像名称
[root@server2 ~]# docker pull nginx
私有仓库管理
创建私有仓库
新建用户
授权新建的用户可以维护私有仓库
私有仓库上传下载都需要认证,并且还要指定仓库域名
[root@server1 ~]# docker tag ubuntu:latest reg.westos.org/westos/ubuntu:latest
[root@server1 ~]# docker push reg.westos.org/westos/ubuntu:latest[root@server2 ~]# docker login reg.westos.org
Username: wxh
Password: ***[root@server2 ~]# docker pull reg.westos.org/westos/ubuntu
仓库附加说明
harbor仓库docker-compose在每次主机重启时会开机自启,但经常有某个容器没能自启的情况发生,使harbor产生异常,因此每次重启后手动运行一下docker-compose start
[root@server1 harbor]# docker-compose down #删除harbor仓库,停止所有容器并全部删除,但是挂载数据不会消失
[root@server1 harbor]# docker-compose logs #查看日志添加新模块部署harbor
[root@server1 harbor]# ./install.sh --with-chartmuseum
###6.Docker网络###
docker network子命令
• connect 连接容器到指定网络
• create 创建网络
• disconnect 断开容器与指定网络的连接
• inspect 显示指定网络的详细信息
• ls 显示所有网络
• rm 删除网络
(1)原生网络
docker的镜像是令人称道的地方,但网络功能还是相对薄弱的部分。
docker安装后会自动创建3种网络:bridge、host、none
可以使用以下命令查看[root@server1 harbor]# docker network ls NETWORK ID NAME DRIVER SCOPE fe748c728e1e bridge bridge local ef483e4d5ab4 host host local 0a4064c6ebb6 none null local
①bridge
docker安装时会创建一个名为 docker0 的Linux bridge,新建的容器会自动桥接到这个接口。
bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的。
容器通过宿主机的NAT规则后可以访问外网。
默认使用桥接模式,桥接到docker0上
[root@server1 ~]# docker run -d --name demo nginx
[root@server1 ~]# yum install -y bridge-utils
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242ea04cb8d no veth6725905
②host
host 网络模式需要在容器创建时指定 --network=hosthost模式可以让容器共享宿主机网络栈,不会新建虚拟网卡,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
host模式,容器和宿主机共享同一网络栈,不会新建虚拟网卡
[root@server1 ns]# docker run -it --rm --network host busybox
host模式下不能同时运行两次同一个容器,会发生冲突
因为共用一个网络栈,端口资源也是共用的
③none
none模式是指禁用网络功能,只有lo接口,在容器创建时使用 --network=none指定。
none禁用网络,只用回环接口
[root@server1 harbor]# docker run -it --rm --network none busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
/ #
(2)自定义网络
自定义网络模式,docker提供了三种自定义网络驱动:
• bridge
• overlay
• macvlan
bridge驱动类似默认的bridge网络模式,但增加了一些新的功能,overlay和macvlan是用于创建跨主机网络。
建议使用自定义的网络来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。
①自定义网络
自定义网络
docker network create my_net1
查看
[root@server1 ~]# docker network inspect my_net1
②自定义网络段
自定义网络地址段
[root@k8s1 ~]# docker network create --subnet 10.0.0.0/24 --gateway 10.0.0.1 my_net2
查看
[root@server1 ~]# docker network inspect my_net2
③自定义网络内嵌DNS解析
使用--ip参数可以指定容器ip地址,但必须是在自定义网桥上,默认的 bridge模式不支持,同一网桥上的容器是可以互通的。
桥接到不同网桥上的容器,彼此是不通信的。
docker在设计上就是要隔离不同network的
那么如何使两个不同网桥的容器通信呢:
使用 docker network connect命令为vm1添加一块my_net2 的网卡。
自定义网络内嵌dns解析同一网桥上的容器是可以互通的
[root@server1 ~]# docker run -d --name web1 --network my_net1 nginx
[root@server1 ~]# docker run -it --rm --network my_net1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
97: eth0@if98: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:14:00:03 brd ff:ff:ff:ff:ff:ffinet 172.20.0.3/16 brd 172.20.255.255 scope global eth0valid_lft forever preferred_lft forever
/ # ping web1
PING web1 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.076 ms
64 bytes from 172.20.0.2: seq=1 ttl=64 time=0.064 ms
64 bytes from 172.20.0.2: seq=2 ttl=64 time=0.062 ms不同网络之间是被隔离的,默认不能通信
[root@server1 ~]# docker run -it --rm --network my_net2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
85: eth0@if86: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ffinet 10.0.0.2/24 brd 10.0.0.255 scope global eth0valid_lft forever preferred_lft forever/ # ping web1
ping: bad address 'web1'不同网络之间的通信需要附加虚拟网卡
[root@server1 ~]# docker run -it --rm --network my_net2 --name demo busybox
/ # ping web1
ping: bad address 'web1'ctrl+pq 快捷键退出容器终端把demo接入到my_net1网络
[root@server1 ~]# docker network connect my_net1 demo再进入容器
[root@server1 ~]# docker attach demo
/ # ping web1
PING web1 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.079 ms
(3)Docker容器通信
①joined容器
Joined容器一种较为特别的网络模式
在容器创建时使用--network=container:vm1指定(vm1指定的是运行的容器名)
处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用localhost 高效快速通信。由于共享同一份网络栈,所以joined模式不能用启动同一个容器,会发生冲突
两个容器共享一个网络栈
[root@server1 ~]# docker run -it --rm --network container:web1 --name web2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
19: eth0@if20: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:14:00:02 brd ff:ff:ff:ff:ff:ffinet 172.20.0.2/16 brd 172.20.255.255 scope global eth0valid_lft forever preferred_lft forever
②--link
--link 可以用来链接2个容器
--link的格式:
--link <name or id>:alias
name和id是源容器的name和id,alias是源容器在link下的别名。
现在不常用,了解即可
[rootaserver1~l#docker run -d nginx
d61a3e7613f9417d29cc97666a80186758581e5f4b067c1de7014ef755ee9ba2
[rootaserver1~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED PORTS NAMES
d61a3e7613f93 nginx "nginx-g'daemon of." 3seconds ago 80/tcp hopeful golick[root@server1 ~]# docker run -it --name ml --link hopeful golick:web ubuntu
roota137597ff4039:/#ping web
PING web(172.17.0.2)56(84)bytes of data.
64 bytes from web(172.17.0.2):icmp seg=l ttl=64 time=0.054 ms
64 bytes from web(172.17.0.2):icmp seg=2 ttl=64 time=0.036 ms
③容器访问外网
容器如何访问外网是通过iptables的SNAT实现的
④外部主机访问容器
外网如何访问容器:
• 端口映射
• -p 选项指定映射端口
外网访问容器用到了docker-proxy和iptables DNAT(双重保障)
• 宿主机访问本机容器使用的是iptables DNAT
• 外部主机访问容器或容器之间的访问是docker-proxy实现
外部主机访问容器可以通过DNAT规则和docker-proxy,只要有一种正常就能通信
[root@server1 ~]# docker rm -f web1
[root@server1 ~]# docker run -d --name web1 -p 80:80 nginx
[root@server1 ~]# iptables -t nat -nL
...
Chain POSTROUTING (policy ACCEPT) #容器访问外网通过SNAT策略
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.21.0.0/16 0.0.0.0/0
MASQUERADE all -- 10.0.0.0/24 0.0.0.0/0
MASQUERADE all -- 172.20.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:80Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 #DNAT规则
DNAT规则和docker-proxy策略任何一个down掉,另一个会自动起作用,两个都在时外部访问优先走docker-proxy
重启容器后,DNAT规则和docker-proxy策略会自动恢复
⑤跨主机容器通信
跨主机网络解决方案
• docker原生的overlay和macvlan
• 第三方的flannel、weave、calico
众多网络方案是如何与docker集成在一起的
• libnetwork docker容器网络库
• CNM (Container Network Model)这个模型对容器网络进行了抽象
CNM分三类组件
• Sandbox:容器网络栈,包含容器接口、dns、路由表。 (namespace)
• Endpoint:作用是将sandbox接入network (veth pair)
• Network:包含一组endpoint,同一network的endpoint可以通信。
macvlan 网络方案实现• Linux kernel 提供的一种网卡虚拟化技术。• 无需 Linux bridge ,直接使用物理接口,性能极好。
在两台docker主机上各添加一块网卡
开启混杂模式并激活
[root@server1 ~]# ip link set eth1 promisc on
[root@server1 ~]# ip link set up eth1创建macvlan网络
[root@server1 ~]# docker network create -d macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.1 -o parent=eth1 macvlan1用创建的网络运行容器
[root@server1 ~]# docker run -it --rm --network macvlan1 --ip 10.0.0.11 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
108: eth0@if107: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:0a:00:00:0b brd ff:ff:ff:ff:ff:ffinet 10.0.0.11/24 brd 10.0.0.255 scope global eth0server2上以此类推
[root@server2 ~]# ip link set eth1 promisc on
[root@server2 ~]# ip link set up eth1[root@server2 ~]# docker network create -d macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.1 -o parent=eth1 macvlan1[root@server2 ~]# docker run -it --rm --network macvlan1 --ip 10.0.0.12 busybox
i/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
9: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:0a:00:00:0c brd ff:ff:ff:ff:ff:ffinet 10.0.0.12/24 brd 10.0.0.255 scope global eth0valid_lft forever preferred_lft forever
/ # ping 10.0.0.11
PING 10.0.0.11 (10.0.0.11): 56 data bytes
64 bytes from 10.0.0.11: seq=0 ttl=64 time=1.117 ms
64 bytes from 10.0.0.11: seq=1 ttl=64 time=1.366 ms
server2以此类推
macvlan网络结构分析
• 没有新建linux bridge
• 容器的接口直接与主机网卡连接,无需NAT或端口映射。
macvlan会独占主机网卡,但可以使用vlan子接口实现多macvlan网络
• vlan可以将物理二层网络划分为4094个逻辑网络,彼此隔离,vlan id取 值为1~4094。
macvlan网络间的隔离和连通
• macvlan网络在二层上是隔离的,所以不同macvlan网络的容器是不能通信的。
• 可以在三层上通过网关将macvlan网络连通起来。
• docker本身不做任何限制,像传统vlan网络那样管理即可。
###7.Docke数据卷###
为什么要用数据卷
docker分层文件系统
• 性能差
• 生命周期与容器相同
docker数据卷
• mount到主机中,绕开分层文件系统
• 和主机磁盘性能相同,容器删除后依然保留
• 仅限本地磁盘,不能随容器迁移
docker提供了两种卷:
• bind mount
• docker managed volume
bind mount 与 docker managed volume 对比• 相同点:两者都是 host 文件系统中的某个路径。• 不同点:
(1)数据卷管理
①bind mount
是将主机上的目录或文件mount到容器里。
使用直观高效,易于理解。
使用 -v 选项指定路径,格式 <host path>:<container path>
# docker run -d --name web -v /opt/website:/usr/share/nginx/html nginx
bind mount 默认权限是读写rw,可以在挂载时指定只读ro。
-v选项指定的路径,如果不存在,挂载时会自动创建。
[root@server1 ~]# docker run -it --rm -v /tmp/data1:/data1 -v /tmp/data2:/data2:ro -v /etc/passwd:/mnt/passwd:ro busybox
/ # ls
bin data1 data2 dev etc home lib lib64 mnt proc root sys tmp usr var
/ # cd /data1/
/data1 # ls
/data1 # touch file1
/data1 # ls
file1
/data1 # cd ..
/ # cd /data2/
/data2 # ls
/data2 # touch file2
touch: file2: Read-only file system
/data2 # cd /mnt/
/mnt # ls
passwd
/mnt # cat passwd
②docker managed volume
bind mount必须指定host文件系统路径,限制了移植性。
docker managed volume 不需要指定mount源,docker自动为容器创建数 据卷目录。
默认创建的数据卷目录都在 /var/lib/docker/volumes 中。
如果挂载时指向容器内已有的目录,原有数据会被复制到volume中。
[root@server1 ~]# docker run -d --name web1 webserver:v3
[root@server1 ~]# docker inspect web1
[root@server1 ~]# cd "/var/lib/docker/volumes/9e6528410eb82a00f89bd1eb5cdda034bb1fbbd0eab1aa36f5ec08fc521a5fae/_data"
[root@server1 _data]# ls
50x.html index.html ##docker managed volume 会复制目标挂载点的数据
查询docker managed volume
[root@server1 volumes]# docker volume ls清理所有数据卷,在清理前需要先删除容器,释放对数据卷的占用
[root@server1 volumes]# docker volume ls | grep -v DRIVER | awk '{system("docker volume rm "$2"")}'
创建数据卷
[root@server1 ~]# docker volume create vol1
vol1
[root@server1 ~]# docker volume ls
DRIVER VOLUME NAME
local vol1
[root@server1 ~]# docker volume inspect vol1
[{"CreatedAt": "2023-03-05T09:21:01+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/vol1/_data","Name": "vol1","Options": null,"Scope": "local"}
]使用bind mount方式挂载数据卷,和docker managed volume结合在一起用
[root@server1 ~]# docker run -d --name web1 -v vol1:/usr/local/nginx/html webserver:v3还能在挂载时控制权限
[root@server1 ~]# docker rm -f web1
web1
[root@server1 ~]# docker run -d --name web1 -v vol1:/usr/local/nginx/html:ro webserver:v3共享卷
[root@server1 ~]# docker create -v /tmp/data1:/data1:rw -v /tmp/data2:/data2:ro -v /etc/passwd:/mnt/passwd:ro --name datavol busybox[root@server1 ~]# docker run -it --rm --volumes-from datavol busybox
(2)卷插件
docker 卷默认使用的是local类型的驱动,只能存在宿主机,跨主机的 volume就需要使用第三方的驱动,可以查看以下链接:
https://docs.docker.com/engine/extend/legacy_plugins/#volume - plugins
docker官方只提供了卷插件的api,开发者可以根据实际需求定制卷插件驱动。
https://docs.docker.com/engine/extend/plugins_volume/#volume - plugin-protocol
Docker Plugin 是以Web Service的服务运行在每一台Docker Host上的,通过HTTP协议传输RPC风格的JSON数据完成通信。
Plugin的启动和停止,并不归Docker管理,Docker Daemon依靠在缺省路径下查找UnixSocket文件,自动发现可用的插件。
当客户端与Daemon交互,使用插件创建数据卷时,Daemon会在后端 找到插件对应的socket 文件,建立连接并发起相应的API请求,最终结合Daemon自身的处理完成客户端的请求。
###8.Docker安全###
(1)理解Docker安全
Docker容器的安全性,很大程度上依赖于Linux系统自身,评估Docker 的安全性时,主要考虑以下几个方面:
• Linux内核的命名空间机制提供的容器隔离安全
• Linux控制组机制对容器资源的控制能力安全。
• Linux内核的能力机制所带来的操作权限安全
• Docker程序(特别是服务端)本身的抗攻击性。
• 其他安全增强机制对容器安全性的影响。
①命名空间隔离的安全
• 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的命名空间。命名空间提供了最基础也最直接的隔离。
• 与虚拟机方式相比,通过Linux namespace来实现的隔离不是那么彻底。
• 容器只是运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作系统内核。
• 在 Linux 内核中,有很多资源和对象是不能被Namespace化的, 比如:时间。
②控制组资源控制的安全
• 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的控制组策略集合。
• Linux Cgroups提供了很多有用的特性,确保各容器可以公平地分享主机的内存、CPU、磁盘IO等资源。
• 确保当发生在容器内的资源压力不会影响到本地主机系统和其他容器,它在防止拒绝服务攻击(DDoS)方面必不可少。
③内核能力机制
• 能力机制(Capability)是Linux内核一个强大的特性,可以提供细粒度的权限访问控制。
• 大部分情况下,容器并不需要“真正的”root权限,容器只需要少数的能力即可。
• 默认情况下,Docker采用“白名单”机制,禁用“必需功能”之外的其他权限。
④Docker服务端防护
• 使用Docker容器的核心是Docker服务端,确保只有可信的用户才能访问到Docker服务。
• 将容器的root用户映射到本地主机上的非root用户,减轻容器和主机之间因权限提升而引起的安全问题。
• 允许Docker 服务端在非root权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程只允许在特定范围内进行操作。
⑤其他安全特性
• 在内核中启用GRSEC和PAX,这将增加更多的编译和运行时的安全检查;并且通过地址随机化机制来避免恶意探测等。启用该特性不需要Docker进行任何配置。
• 使用一些有增强安全特性的容器模板。
• 用户可以自定义更加严格的访问控制机制来定制安全策略。
• 在文件系统挂载到容器内部时,可以通过配置只读模式来避免容器内的应用通过文件系统破坏外部环境,特别是一些系统运行状态相关的目录。
(2) 容器资源控制Linux Cgroups
Linux Cgroups 的全称是 Linux Control Group。
• 是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
• 对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
Linux Cgroups 给用户暴露出来的操作接口是文件系统。
• 它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。
• 执行此命令查看:mount -t cgroup
• 在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。
• 在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录)。
• 控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的 参数指定。
①CPU资源限制
cpu_period 和 cpu_quota 这两个参数需要组合使用,用来限制进 程在长度为 cpu_period 的一段时间内,只能被分配到总量为 cpu_quota 的 CPU 时间,以上设置表示20%的cpu时间。
[root@server1 cpu]# docker run -it --rm --cpu-period 100000 --cpu-quota 20000 ubuntu ###--cpu-period是cpu总分片 --cpu-quota是cpu配额 这里配额占总的20%
root@433a1612a171:/# dd if=/dev/zero of=/dev/null &
②CPU优先级
[root@server1 cpu]# docker run -it --rm ubuntu
root@0280fc49f2d0:/# dd if=/dev/zero of=/dev/null &[root@server1 cpu]# docker run -it --rm --cpu-shares 100 ubuntu
root@b75b4d5066b8:/# dd if=/dev/zero of=/dev/null &测试时只保留一个cpu核心可用,只有争抢cpu资源时优先级才会生效
[root@server1 cpu1]# pwd
/sys/devices/system/cpu/cpu1
[root@server1 cpu1]# echo 0 > online
③内存资源限额
容器可用内存包括两个部分:
物理内存和swap交换分区。
• --memory设置内存使用限额
• --memory-swap设置swap交换分区限额
①用命令行控制内存限额
[root@server1 ~]# docker run -d --name demo --memory 200M --memory-swap=200M nginx②直接通过控制cgroup控制内存限额
创建一个容器目录
[root@server1 memory]# pwd
/sys/fs/cgroup/memory
[root@server1 memory]# mkdir x1
[root@server1 memory]# cd x1/
设置x1控制器的内存限额为200M
[root@server1 x1]# echo 209715200 > memory.limit_in_bytes[root@server1 x1]# yum install -y libcgroup-tools.x86_64
[root@server1 x1]# cd /dev/shm/
[root@server1 shm]# cgexec -g memory:x1 dd if=/dev/zero of=bigfile bs=1M count=300
[root@server1 shm]# free -mtotal used free shared buff/cache available
Mem: 1980 187 1080 206 712 1389
Swap: 2047 103 1944
多出的100m会写入swap[root@server1 shm]# cd /sys/fs/cgroup/memory/x1/
设置x1控制器内存+swap分区限额一共为200M
[root@server1 x1]# echo 209715200 > memory.memsw.limit_in_bytes
[root@server1 x1]# cd -
/dev/shm
[root@server1 shm]# cgexec -g memory:x1 dd if=/dev/zero of=bigfile bs=1M count=300
Killed控制用户内存
[root@server1 shm]# useradd wxh
[root@server1 shm]# vim /etc/cgrules.conf
wxh memory x1/[root@server1 shm]# systemctl start cgred.service
[root@server1 shm]# su - wxh
[wxh@server1 ~]$ cd /dev/shm/
[wxh@server1 shm]$ dd if=/dev/zero of=bigfile bs=1M count=200
Killed
④磁盘IO限制
--device-write-bps限制写设备的bps
目前的block IO限制只对direct IO有效。(不使用文件缓存)
[root@server1 ~]# docker run -it --rm --device-write-bps /dev/sda:30MB ubuntu
root@3226b0fc6231:/# dd if=/dev/zero of=bigfile bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 3.31722 s, 31.6 MB/s
(3)Docker安全加固
①lxcfs安全隔离
利用LXCFS增强docker容器隔离性和资源可见性
[root@server1 ~]# yum install lxcfs-2.0.5-3.el7.centos.x86_64.rpm
[root@server1 ~]# lxcfs /var/lib/lxcfs &
[root@server1 ~]# docker run -it -m 256m \
> -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
> -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
> -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
> -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
> -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
> -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
> ubuntu
root@45edbc92cc1d:/# free -mtotal used free shared buff/cache available
Mem: 256 0 255 9 0 255
Swap: 256 0 256
②容器特权
设置特权级运行的容器:--privileged=true
• 有的时候我们需要容器具备更多的权限,比如操作内核模块,控制swap交 换分区,挂载USB磁盘,修改MAC地址等。
设置容器白名单:--cap-add
• --privileged=true 的权限非常大,接近于宿主机的权限,为了防止用户的滥用,需要增加限制,只提供给容器必须的权限。此时Docker 提供了权限白名单的机制,使用--cap-add添加必要的权限。
• capabilities手册地址: • http://man7.org/linux/man -pages/man7/capabilities.7.html
默认容器内的用户是受限的
[root@server1 ~]# docker run -it --rm busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ffinet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever
/ # id
uid=0(root) gid=0(root) groups=0(root),10(wheel)
/ # ip link set down eth0
ip: SIOCSIFFLAGS: Operation not permitted
/ # fdisk -l开启容器特权
[root@server1 ~]# docker run -it --rm --privileged busybox
/ # fdisk -l
Disk /dev/sda: 20 GB, 21474836480 bytes, 41943040 sectors
2610 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytesDevice Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/sda1 * 0,32,33 130,170,40 2048 2099199 2097152 1024M 83 Linux
/dev/sda2 130,170,41 1023,254,63 2099200 41943039 39843840 18.9G 8e Linux LVM
Disk /dev/dm-0: 17 GB, 18249416704 bytes, 35643392 sectors
2218 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/dm-0 doesn't contain a valid partition table
Disk /dev/dm-1: 2048 MB, 2147483648 bytes, 4194304 sectors
261 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytesDisk /dev/dm-1 doesn't contain a valid partition table
/ # ip link set down eth0
/ # ip link set up eth0设置容器白名单
[root@server 1 ~]# docker run -it --rm --cap-add=NET_ADMIN busybox
/ # fdisk -l
/ # ip a a 10.0.0.1/24 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ffinet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft foreverinet 10.0.0.1/24 scope global eth0valid_lft forever preferred_lft forever
/ # ip a d 10.0.0.1/24 dev eth0
/ # ip a
安全加固的思路:
(1)保证镜像的安全
• 使用安全的基础镜像
• 删除镜像中的setuid和setgid权限
• 启用Docker的内容信任
• 最小安装原则
• 对镜像进行安全漏洞扫描,镜像安全扫描器:Clair
• 容器使用非root用户运行
(2)保证容器的安全
• 对docker宿主机进行安全加固
• 限制容器之间的网络流量
• 配置Docker守护程序的TLS身份验证
• 启用用户命名空间支持(userns-remap)
• 限制容器的内存使用量
• 适当设置容器CPU优先级
docker安全的遗留问题
(1)主要的内核子系统都没有命名空间,如:
• SELinux
• cgroup
• 在/sys下的文件系统
• /proc/sys, /proc/sysrq-trigger, /proc/irq, /proc/bus
(2)设备没有命名空间:
• /dev/mem
• /dev/sd*文件系统设备
• 内核模块
Docker安全的顶尖开源工具:
• Docker Bench for Security 对照安全基准审计Docker容器的脚本
• Clair API驱动的静态容器安全分析工具,拥有庞大的CVE数据库
• Cilium 内核层可感知API的网络和安全工具
• Anchore 使用CVE数据和用户定义的策略检查容器安全的工具
• OpenSCAP W orkbench 用于为各种平台创建和维护安全策略的环境
• Dagda 用于在Docker容器中扫描漏洞、特洛伊木马、病毒和恶意软件的工具
• Notary 使用服务器加强容器安全的框架,用于以加密方式委派责任
• Sysdig Falco 提供了行为活动监控,可深入了解容器