一、Docker介绍
Docker镜像:使用docker把应用程序和依赖环境打包而成
Docker Container(容器):使用runtime启动起来的Docker镜像。
DevOps:为了解决开发交付与敏捷开发矛盾而形成的
13年成立,15年开始。
Docker1.8之前使用LXC:Linux内核接口,不支持跨平台
namespace和cgroup封装在libcontainer(支持跨平台)
OCI(open container initiative开放容器计划)组织。
libcontainer+CLI=runC与底层交互
containerd提供grpc接口,实现容器上的高级功能,比如镜像管理、容器执行的调用等
Dockerd目前是最上层与CLI交互的进程,接收Cli的请求并与containerd协作。
Docker是一种CS架构的软件产品,可以把代码与依赖打包成镜像,作为交付介质,并且把镜像启动成为容器,提供容器生命周期的管理。
runC和Containerd是docker的核心项目。
二、Docker安装
配置宿主机网卡转发
$ cat <<EOF > /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1#二层网桥需要经过iptables过滤规则
net.ipv4.ip_forward=1#允许路由转发功能打开
$ sysctl -p /etc/sysctl.d/docker.conf#从指定文件加载系统参数
Yum安装配置docker
#下载阿里源repo文件
#curl -o 本地NewName 下载源地址
$ curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
$ curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
$ yum clean all &&yum makecache
#yum会把下载下来的rpm包和header存储在cache中,不会自动清除。yum clean headers,yum clean packages
#yum makecache是把服务器的包信息下载到本地缓存起来。
$ yum install docker-ce -y
$ yum list docker-ce --showduplicates | sort -r#查看版本
$ yum install -y docker-ce-18.09.9#安装指定版本
#输出docker-ce的版本号并将数字当作字符进行排序,查看源中可用版本
安装最新版的19.03
#配置源加速器,docker镜像是在国外,阿里提供了一个加速镜像
https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
mkdir -p /etc/docker
vi /etc/docker/daemon.json#进程加载时读这个文件
{
"registry-mirrors":[
"https://***.mirror.aliyuncs.com"
]
}
#设置开机自启
systemctl enable docker
systemctl daemon-reload
#启动docker
systemctl start docker
#which docker环境变量里的docker文件
#ps aux|grep dockerd#查看进程docker是否启动
#journalctl -fu docker查看docker日志
#docker version查看docker版本
三、三大核心要素及常用操作详解
三大核心要素:镜像(Image)、容器(Container)、仓库(Registry)
仓库:存放镜像的地方
1)公有仓库Docker Hub、阿里、网易
2)私有仓库
- Docker Registry,Docker官方提供的镜像仓库存储服务
- Harbor,是Docker Registry的更高级封装,它除了提供友好的Web UI界面,角色和用户权限管理,用户操作审计等功能
- 镜像访问地址形式registry.devops.com/demo/hello:latest,若没有前面的url地址,则默认寻找Docker Hub中的镜像,若没有tag标签,则使用latest作为标签。比如,docker pull nginx,会被解析成docker.io/library/nginx:latest
- 公有的仓库中,一般存在这么几类镜像
- 操作系统基础镜像(centos,ubuntu,suse,alpine)
- 中间件(Nginx,redis,mysql,tomcat)
- 语言编译环境(python,java,golang)
- 业务镜像(django-demo...)
容器和仓库不会直接交互,都是以镜像为载体来操作。
2、基本命令
docker images#查看基础镜像列表
docker pull nginx:alpine#拉取镜像alpine是版本
#本地构建
docker build . -t my-nginx:ubuntu -f Dockerfile
#如何通过镜像启动容器
docker run --name my-nginx-alpine -d nginx:alpine
#如何知道容器内部运行了什么程序?
#进入容器内部,分配一个tty终端
$ docker exec -ti my-nginx-alpine /bin/sh#进入容器
$ docker exec my-nginx-alpine ls#精准执行一个命令
$ docker ps#查看容器列表
$ pwd#当前所处目录/etc/docker
$ ps axu#查看系统进程 nginx -g daemon off;表示进入的是前台,daemon表示后台
$ exit#退出
#创建Dockerfile
#告诉docker使用哪个基础镜像作为模版,后续命令都以这个镜像为基础,ubuntu可以是镜像的地址
FROM ubuntu
#RUN命令会在上面指定的镜像里执行命令
RUN apt-get update && apt install -y nginx
#告诉docker,启动容器时执行如下命令
CMD ["/usr/sbin/nginx","-g","daemon off;"]
docker build . -t my-nginx:ubuntu -f Dockerfile#点是目录
宿主机中如何访问容器服务
$ docker rm -f my-nginx-alpine#删除旧服务
$ docker run --name my-nginx-alpine -d -p 8080:80 nginx:alpine#8080是宿主机,映射容器80端口
$ curl 192.168.1.12:8080
导出镜像到文件中
$ docker save -o nginx-alpine.tar nginx:alpine
从文件中加载镜像
$ docker load -i nginx-alpine.tar
commit、export不建议使用,没有历史记录,不能修改
$ docker info#docker信息
使用docker镜像启动镜像仓库服务
$ docker run -d -p 5000:5000 -name registry registry:2#添加--restart always重启的时候,它会自动启动起来-d是后台启动
$ docker run --memory=500m nginx:alpine#资源限制,最大可用内存500M
docker tag nginx:alpine localhost:5000/nginx:alpine#把本地的镜像推到registry仓库,tag理解为快捷方式
docker push localhost:5000/nginx:alpine#推到镜像仓库
查看仓库内镜像元数据
$ curl -X GET http://x.x.x.x:5000/v2/_catalog#访问镜像名称
$ curl -X GET http://x.x.x.x:5000/v2/nginx/tags/list#访问镜像和标签标签
#docker默认不允许向http的仓库地址推送,如何做成https的,参考:https://docs.docker.com/registry/deploying/#run-an-externally-accessible-registry
#我们没有可信任证书机构颁发的证书和域名,自签名证书需要在每个节点中拷贝证书文件,比较麻烦,因此问我我们通过配置daemon的方式,来跳过证书的验证
$ cat /etc/docker/daemon.json
{
"registry-mirrors":["https://x.mirror.aliyuncs.com],
"insecure-registries":["x.x.x.x:5000"]
}
$ systemctl restart docker#重启docker
$ docker ps -a#查看所有
$ docker start my-nginx-alpine#启动镜像
$ docker rmi localhost:5000/nginx:alpine#删除镜像
容器数据持久化
##挂载主机目录
$ docker run --name nginx -d -v /opt:/opt -v /var/log:/var/log nginx:alpine
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d -v /opt/mysql/:/var/lib/mysql mysql:5.7#左边宿主机,右边容器
###使用volumes卷
$ docker volume ls
$ docker volume create my-vol
#同上,只是把磁盘换成了盘符卷来使用,不推荐
#验证数据共享
$ docker exec -ti nginx2 命令
进入容器或者执行容器内的命令
$ docker exec -ti <container_id_or_name> /bin/sh
$ docker exec <container_id_or_name> 命令
主机与容器之间拷贝数据
#主机拷贝到容器
$ docker cp /tmp/test.txt nginx:/tmp
#容器拷贝到主机
$ docker cp nginx:/tmp/test.txt ./
挂载已有的数据,重新创建镜像仓库容器
$ scp -P 9000 /d/tmp/registry.tar.gz x.x.x.x:/tmp#复制文件
#解压离线镜像文件
tar zxf registry.tar.gz -C /opt
###删除当前镜像仓库容器
$ docker rm -f registry
##使用docker镜像启动镜像仓库服务
$ docker run -d -p 5000:5000 --restart always -v /opt/registry:/var/lib/registry --name registry registry:2
四、Django应用容器化实践
构建命令
$ docker build . -t ImageName:ImageTag -f Dockerfile
Dockerfile文件
#this my first django Dockerfile
#Version 1.0
#Base images 基础镜像
FROM centos:centos7.5.1804
#MAINTAINER维护者信息
LABEL maintainer="whoami@qq.com"
#ENV设置环境变量
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
#RUN 执行一下命令
RUN curl -so /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo&& rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.e17.ngx.noarch.rpm
RUN yum install -y python36 python3-devel gcc pcre-devel zlib-devel make net-tools nginx
#工作目录
WORKDIR /opt/myblog
#拷贝文件至工作目录.是当前目录的所有,第二个.是容器工作目录
COPY . .
#拷贝nginx配置文件
COPY myblog.conf /etc/nginx
#安装依赖的插件
RUN pip3 install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt
RUN chmod +x run.sh && rm -rf ~/.cache/pip
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
#EXPOSE映射端口
EXPOSE 8002
#容器启动时执行命令
CMD ["./run.sh"]
run.sh文件
#/bin/bash
uwsgi --ini ./uwsgi.ini
nginx -c /etc/nginx/myblog.conf -g "daemon off;'
requirements.txt文件
PyMySQL==0.9.3
Django==2.1.8
uwsgi==2.0.18
docker build . -t myblog:v1 -f Dockerfile
mysql应该和之前的镜像分开,因为它应该单进程运行
$ docker run -d -p 3306:3306 --name mysql -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
启动Django应用
##启动容器
$ docker run -d -p 8002:8002 --name myblog -e MYSQL_HOST=x.x.x.x -e MYSQL_USER=root -e MYSQL_PASSWD=123456 myblog:v1
#创建超级用户
$ docker exec -ti myblog python3 manage.py createsuperuser
##收集静态文件
$ docker exec -ti myblog python3 manage.py collectstatic
mysql重做镜像,否则配置每次都要进行挂载
#dockerfiles/mysql/Dockerfile文件
FROM mysql:5.7
COPY my.cnf /etc/mysql/my.cnf#添加本地文件到镜像和ADD功能一样,ADD多了解压
###CMD或者ENTRYPOINT默认继承 ,ENTRYPOINT先执行,则CMD作为参数传递
#删除旧的mysql容器,使用新镜像启动,不用再此初始化
docker build . -t mysql:5.7-utf8#-f Dockerfile假如名字默认是这个可以省略
docker tag mysql:5.7-utf8 xxx.xxx.xx.xx:5000/mysql:5.7-utf8
docker push xxx.xxx.xx.xx:5000/mysql:5.7-utf8#上传到仓库占空间,可以不push
docker rm -f mysql
rm -rf /opt/mysql/
docker run -d -p 3306:3306 --restart=always --name mysql -v /opt/mysql/:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7-utf8
五、实现原理
虚拟化核心需要解决的问题:资源隔离和资源限制
- 虚拟化硬件虚拟化技术,通过一个hypervisor层实现对资源的彻底隔离
- 容器则是操作系统级别的虚拟化,利用的是内核的Cgroup和Namespace特性,此功能完全通过软件实现
1、Namespace资源隔离
命名空间是全局资源的一种抽象,将资源放到不同的命名空间中,各个命名空间中的资源是相互隔离的
| 分类 | 系统调用参数 | 相关内核版本 |
| Mount namespaces | CLONE_NEWNS | Linux 2.4.19 |
| UTS namespaces | CLONE_NEWUTS | Linux 2.6.19 |
| IPC namespaces | CLONE_NEWIPC | Linux 2.6.19 |
| PID namespaces | CLONE_NEWPD | Linux 2.6.24 |
| Network namespaces | CLONE_NEWNET | Linux 2.6.24-2.6.29 |
| User namespaces | CLONE_NEWUSER | Linux 2.6.23-3.8 |
echo $$#命名空间id
ls -l /proc/$$/ns#显示当前命名空间里的
docker在启动一个容器的时候,会调用linux kernel Namespace的接口,来创建一块虚拟空间,创建的时候,可以支持设置下面的这几种(可以随意选择),docker默认都设置。
- pid:用于进程隔离(PID:进程ID)
- net:管理网络接口(NET:网络)
- ipc:管理对IPC资源的访问(IPC:进程间通信(信号量、消息队列和共享内存))
- mnt:管理文件系统挂载点(MNT:挂载)
- uts:隔离主机和域名
- user:隔离用户和用户组
2、CGroup资源限制
通过namespace可以保证容器之间的隔离,但是无法控制每个容器可以占用多少资源,如果其中的某一个容器正在执行CPU密集型的任务,那么就会影响其他容器中任务的性能与执行效率,导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了解决进程虚拟资源隔离之后的主要问题。int clone()
Control Groups(简称CGroups)就是能够隔离宿主机器上的物理资源,例如CPU、内存、磁盘I/O和网路带宽。每一个CGroup都是一组被相同的标准和参数限制的进程。而我们需要做的,其实就是把容器这个进程加入到指定的Cgroup中。
3、UnionFS其实是一种为Linux操作系统设计的用于把多个文件系统联合到同一个挂载点的文件系统服务。它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,整个联合的过程被称为联合挂载(Union Mount)
docker info#查看存储驱动storage driver
docker是分层读取的,Dockerfile每一个命令即是一层
六、网络模式
我们在使用docker run创建容器时,可以用--net选项指定容器的网络模式,Docker有以下四种网路模式:
- bridge模式,使用--net=bridge指定,默认设置
- host模式,使用--net=host指定,容器内部网络空间共享宿主机的空间,效果类似直接在宿主机上启动一个进程,端口信息和宿主机共用
- container模式,使用--net=container:NAME_or_ID指定,指定容器与特定容器共享命名空间
- none模式,使用--net=none指定,网络模式为空,即仅保留网络命名空间,但是不做任何网络相关的配置(网卡、IP、路由等)
1、bridge模式
网桥模式:可以看作一个二层交换机
Linux中能够起到虚拟交换机作用的网络设备,是网桥,它是一个工作在数据链路层(Data Link)的设备,主要功能是根据MAC地址将数据包转发到网桥的不同端口上。
yum install -y bridge-utils#没有的网桥,可以安装下,docker创建的时候创建
brctl show
brctl showmacs docker0#展示mac地址,虚拟网桥
route -n#展示网关
ip a#所有网卡
veth pair相当于一条网线
docker exec -ti test1 cat /sys/class/net/eth0/ifindex#查看网卡号
#主机中找到虚拟网卡后面的这个@ifxx的值,如果是同一个值,说明这个虚拟网卡和这个容器的eth0网卡是配对的
if a |grep @if
整理脚本,快速查看对应:
for container in $(docker ps -q);|do
iflink=`docker exec -it $container sh -c 'cat /sys/class/net/eth0/iflink'`#容器网卡号
iflink=`echo $iflink|tr -d '\r'`
veth=`grep -l $iflink /sys/class/net/veth*/ifindex`
veth=`echo $veth|sed -e 's;^.*net/\(.*\)/ifindex$;\1;'`
echo $container:$veth
done
2、None模式
只会创建对应的网络空间,不会配置网络堆栈(网卡、路由等)
docker run -it --rm --name=network-none --net=none nginx:alpine sh#容器退出,容器直接会被删除
在宿主机中操作
#创建虚拟网卡对
ip link add A type veth peer name B
#A端插入到docker0网桥
brctl addif docker0 A
ip link set A up
docker inspect network-none|grep -i pid#不区分大小写,输出pid
ll /proc/14464/ns#获取命名空间
ll /proc/$$/ns#宿主机命名空间
#在宿主机操作容器中的命名空间需要借助ip netns
ip netns list
#B端插入到network-none容器中,需要借助ip netns,因此需要显示的创建命名network namespace
PID=$(docker inspect -f '{{.State.Pid}}' network-none)#-f是go语言的格式化输出PID
mkdir -p /var/run/netns
ln -s /proc/$PID/ns/net /var/run/netns/$PID#建立软连接
#B端放到容器的命名空间
ip link set B netns $PID
ip netns exec $PID ip link set dev B name eth0#修改设备名称为eth0,和docker默认行为一致
ip netns exec $PID ip link set eth0 up
#设置ip
ip netns exec $PID ip addr add x.x.x.x/16 dev eth0
#添加默认路由,指定给docker0网桥
ip netns exec $PID ip route add default via x.x.x.x
docker inspect network-none|grep -i pid#查看容器进程id
iptables -t nat -nvL PREROUTING#查看iptables本地规则
ip route show table local type local#查看哪些包走了这个本地规则
iptables -t nat -nvL POSTROUTING# 出口链
SNAT#自动替换源地址,直接转换成出口的ip地址
抓包演示:
#首先访问宿主机的8088端口,我们抓一下宿主机的eth0
$ tcpdump -i eth0 port 8088 -w host.cap#抓包写到host.cap文件里
#容器内安装tcpdump
$ sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories#读文件写入后面的文件中
$ apk add tcpdump
$tcpdump -i eth0 port 80 -w container.cap
docker cp test:/文件名 ./#拷贝容器的文件到宿主机
scp -P 9000 172.21.51.67:/tmp/*.cap /tmp/
cap包可以在wireshark上进行合并包查看
3、Host模式
docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
容器启动后,会默认监听3306端口,由于网路模式上host,因为直接通过宿主机的3306端口进行访问服务,效果等同于在宿主机中直接启动mysqld的进程,共享网络端口,不建议使用
4、Container模式
docker run -ti --rm --net=container:mysql busyhox sh#共享网络命名空间(两个容器,k8s实现网络的形式)
docker rm $(docker ps -aq)#清理主机上所有退出的容器
docker run --rm -ti <image_id> sh#进入容器后,手动执行对应的ENTRYPOINT或者CMD命令,这样即使出错,容器也不会退出,因为bash作为1号进程,我们只要不退出容器,该容器就不会自动退出。
七、重装遇到的问题
重新安装docker,总是挂起状态。
/Applications/Docker.app/Contents/MacOS/Docker --uninstall
参考:Docker doesn't start (no GUI) · Issue #1326 · docker/for-mac · GitHub
1715

被折叠的 条评论
为什么被折叠?



