走进 Docker 的世界

走进 Docker 的世界

介绍docker的前世今生,了解docker的实现原理,以Django项目为例编写最佳的Dockerfile构建镜像。学习docker的概念及基本操作并构建业务镜像,通过抓包的方式掌握Docker最常用的bridge网络模式的通信。

认识docker

怎么出现的

  • 提供轻量、高效的虚拟化能力
    Docker 公司位于旧金山,原名dotCloud,底层利用了Linux容器技术(LXC)(在操作系统中实现资源隔离与限制)。为了方便创建和管理这些容器,dotCloud 开发了一套内部工具,之后被命名为“Docker”。Docker就是这样诞生的。

Hypervisor: 一种运行在基础物理服务器和操作系统之间的中间软件层,可允许多个操作系统和应用共享硬件 。常见的VMware的 Workstation 、ESXi、微软的Hyper-V或者思杰的XenServer。

Container Runtime:通过Linux内核虚拟化能力管理多个容器,多个容器共享一套操作系统内核。因此摘掉了内核占用的空间及运行所需要的耗时,使得容器极其轻量与快速。

  • 软件交付过程中的环境依赖

几个知识点

  • 可以把应用程序代码及运行依赖环境打包成镜像,作为交付介质,在各环境部署
  • 可以将镜像(image)启动成为容器(container),并且提供多容器的生命周期进行管理(启、停、删)
  • container容器之间相互隔离,且每个容器可以设置资源限额
  • 提供轻量级虚拟化功能,容器就是在宿主机中的一个个的虚拟的空间,彼此相互隔离,完全独立
  • CS架构的软件产品

版本管理

  • Docker 引擎主要有两个版本:企业版(EE)和社区版(CE)

  • 每个季度(1-3,4-6,7-9,10-12),企业版和社区版都会发布一个稳定版本(Stable)。社区版本会提供 4 个月的支持,而企业版本会提供 12 个月的支持

  • 每个月社区版还会通过 Edge 方式发布月度版

  • 从 2017 年第一季度开始,Docker 版本号遵循 YY.MM-xx 格式,类似于 Ubuntu 等项目。例如,2018 年 6 月第一次发布的社区版本为 18.06.0-ce

发展史

13年成立,15年开始,迎来了飞速发展。

Docker 1.8之前,使用LXC,Docker在上层做了封装, 把LXC复杂的容器创建与使用方式简化为自己的一套命令体系。

之后,为了实现跨平台等复杂的场景,Docker抽出了libcontainer项目,把对namespace、cgroup的操作封装在libcontainer项目里,支持不同的平台类型。

2015年6月,Docker牵头成立了 OCI(Open Container Initiative开放容器计划)组织,这个组织的目的是建立起一个围绕容器的通用标准 。 容器格式标准是一种不受上层结构绑定的协议,即不限于某种特定操作系统、硬件、CPU架构、公有云等 , 允许任何人在遵循该标准的情况下开发应用容器技术,这使得容器技术有了一个更广阔的发展空间。

OCI成立后,libcontainer 交给OCI组织来维护,但是libcontainer中只包含了与kernel交互的库,因此基于libcontainer项目,后面又加入了一个CLI工具,并且项目改名为runC (https://github.com/opencontainers/runc ), 目前runC已经成为一个功能强大的runtime工具。

Docker也做了架构调整。将容器运行时相关的程序从docker daemon剥离出来,形成了containerd。containerd向上为Docker Daemon提供了gRPC接口,使得Docker Daemon屏蔽下面的结构变化,确保原有接口向下兼容。向下通过containerd-shim结合runC,使得引擎可以独立升级,避免之前Docker Daemon升级会导致所有容器不可用的问题。

也就是说

  • runC(libcontainer)是符合OCI标准的一个实现,与底层系统交互

  • containerd是实现了OCI之上的容器的高级功能,比如镜像管理、容器执行的调用等

  • Dockerd目前是最上层与CLI交互的进程,接收cli的请求并与containerd协作

小结

1、为了解决软件交付过程中的环境依赖,同时提供一种更加轻量的虚拟化技术,Docker出现了
2、Docker是一种CS架构的软件产品,可以把代码及依赖打包成镜像,作为交付介质,并且把镜像启动成为容器,提供容器生命周期的管理
3、docker-ce,每季度发布stable版本。20.03,20.06,20.09
4、发展至今,docker已经通过制定OCI标准对最初的项目做了拆分,其中runC和containerd是docker的核心项目,理解docker整个请求的流程,对深入理解docker有很大的帮助

安装

配置宿主机网卡转发

## 若未配置,需要执行如下
$ cat <<EOF >  /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1
EOF
$ sysctl -p /etc/sysctl.d/docker.conf

Yum 安装配置docker

## 下载阿里源repo文件
$ 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安装
$ yum install docker-ce -y

## 查看源中可用版本
$ yum list docker-ce --showduplicates | sort -r

## 安装指定版本
##yum install -y docker-ce-18.09.9
 
## 配置源加速
## https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
mkdir -p /etc/docker
vi /etc/docker/daemon.json
{
  "registry-mirrors" : [
    "https://9ai55q0c.mirror.aliyuncs.com"
  ]
}
 
## 设置开机自启
systemctl enable docker 
systemctl daemon-reload
 
## 启动docker
systemctl start docker
 
## 查看docker信息
docker info
 
## docker-client
which docker

## docker daemon
ps aux |grep docker

## containerd
ps aux|grep containerd
systemctl status containerd

核心要素及常用操作详解

三大核心要素:镜像(Image)、容器(Container)、仓库(Registry)

镜像(Image)

打包了业务代码及运行环境的包,是静态的文件,不能直接对外提供服务。

容器(Container)

镜像的运行时,可以对外提供服务。

仓库(Registry)

存放镜像的地方

  • 公有仓库,Docker Hub,阿里,网易...

  • 私有仓库,私人及企业内部搭建

    • 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...)

容器和仓库不会直接交互,都是以镜像为载体来操作。

1.查看镜像列表

$ docker images

2.如何获取镜像

  • 从远程仓库拉取

    $ docker pull nginx:alpine
    $ docker images
    
  • 本地构建

    $ docker build . -t my-nginx:ubuntu -f Dockerfile
    

3.如何通过镜像启动容器

# --name 是自定义的名称
$ docker run --name my-nginx-alpine -d nginx:alpine

4.如何知道容器内部运行了什么程序?

# 进入容器内部,分配一个tty终端
$ docker exec -ti my-nginx-alpine /bin/sh
# ps aux

5.docker怎么知道容器启动后该执行什么命令?
通过docker build来模拟构建一个nginx的镜像

  • 创建Dockerfile

    # 告诉docker使用哪个基础镜像作为模板,后续命令都以这个镜像为基础
    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 run --name my-nginx-ubuntu -d my-nginx:ubuntu
    
  • 进入容器查看进程

    $ docker exec -ti my-nginx-ubuntu /bin/sh
    # ps aux
    

6.如何访问容器内服务

# 进入容器内部
$ docker exec -ti my-nginx-alpine /bin/sh

# ps aux|grep nginx
# curl localhost:80

7.宿主机中如何访问容器服务

# 删掉旧服务,重新启动
$ docker rm -f my-nginx-alpine
$ docker run --name my-nginx-alpine -d -p 8080:80 nginx:alpine
$ curl 10.2.2.10:8080

操作演示

1.查看所有镜像:

$ docker images

2.拉取镜像:

$ docker pull nginx:alpine

3.如何唯一确定镜像:

  • image_id
  • repository:tag
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
my-nginx            ubuntu              33fed20f1cdd        2 hours ago         156MB
ubuntu              latest              4e2eef94cd6b        3 weeks ago         73.9MB
nginx               alpine              6f715d38cfe0        4 weeks ago         22.1MB

4.导出镜像到文件中

$ docker save -o nginx.alpine.tar nginx:alpine
$ ll
total 23040
-rw-r--r-- 1 root root       99 Sep 11 10:03 Dockerfile
-rw------- 1 root root 23586816 Sep 12 08:13 nginx.alpine.tar

5.从文件中加载镜像

$ docker load -i nginx-alpine.tar

6.部署镜像仓库
https://docs.docker.com/registry/

## 使用docker镜像启动镜像仓库服务
$ docker run -d -p 5000:5000 --restart always --name registry registry:2

## 默认仓库不带认证,若需要认证,参考
https://docs.docker.com/registry/deploying/#restricting-access

7.推送本地镜像到镜像仓库中

## 给镜像打上标签
$ docker tag nginx:alpine localhost:5000/nginx:alpine
$ docker push localhost:5000/nginx:alpine

## 镜像仓库给外部访问,不能通过localhost,尝试使用内网地址10.2.2.10:5000/nginx:alpine

$ docker tag nginx:alpine 10.2.2.10:5000/nginx:alpine
$ docker push 10.2.2.10:5000/nginx:alpine
The push refers to repository [10.2.2.10:5000/nginx]

## docker默认不允许向http的仓库地址推送,如何做成https的,参考:https://docs.docker.com/registry/deploying/#run-an-externally-accessible-registry

## 至此没有可信证书机构颁发的证书和域名,自签名证书需要在每个节点中拷贝证书文件,比较麻烦,因此通过配置daemon的方式,来跳过证书的验证:
$ cat /etc/docker/daemon.json
{
  "registry-mirrors" : [
    "https://8xpk5wnt.mirror.aliyuncs.com"
  ],
  "insecure-registries": [
    "10.2.2.10:5000"
  ]
}

$ systemctl restart docker  # 要重启docker
$ docker push 10.2.2.10:5000/nginx:alpine'
$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
my-nginx               ubuntu              33fed20f1cdd        3 hours ago         156MB
ubuntu                 latest              4e2eef94cd6b        3 weeks ago         73.9MB
10.2.2.10:5000/nginx   alpine              6f715d38cfe0        4 weeks ago         22.1MB
nginx                  alpine              6f715d38cfe0        4 weeks ago         22.1MB
localhost:5000/nginx   alpine              6f715d38cfe0        4 weeks ago         22.1MB
registry               2                   2d4f4b5309b1        2 months ago        26.2MB

8.删除镜像

$ docker rmi nginx:alpine

9.查看容器列表

## 查看运行状态的容器列表
$ docker ps

## 查看全部状态的容器列表
$ docker ps -a

10.启动容器

## 后台启动
$ docker run --name nginx -d nginx:alpine

## 映射端口,把容器的端口映射到宿主机中,-p <host_port>:<container_port>
$ docker run --name nginx -d -p 8080:80 nginx:alpine

## 资源限制,最大可用内存500M
$ docker run --memory=500m nginx:alpine

11.容器数据持久化

## 挂载主机目录
$ 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 run --name nginx2 -d -v my-volume:/opt/my-volume nginx:alpine
 $ docker exec -ti nginx2 touch /opt/my-volume/a.txt

## 验证数据共享
$ docker run --name nginx-test -d -v my-volume:/opt/test nginx:alpine
$ docker exec -ti nginx-test ls /opt/test/
a.txt

12.进入容器或者执行容器内的命令

$ docker exec -ti <container_id_or_name> /bin/sh
$ docker exec <container_id_or_name> hostname

13.主机与容器之间拷贝数据

## 主机拷贝到容器
$ echo '123' > /tmp/test.txt
$ docker cp /tmp/test.txt nginx2:/tmp
$ docker exec -ti nginx2 cat /tmp/test.txt
123

## 容器拷贝到主机
docker cp nginx2:/tmp/test.txt ./

14.挂载已有的数据,重新创建镜像仓库容器

## 解压离线镜像文件
$ tar zxf registry.tar.gz -C /opt

## 删除当前镜像仓库容器
$ docker rm -f registry

## 使用docker镜像启动镜像仓库服务, --restart always 是docker每次重启容器随之自启动
$ docker run -d -p 5000:5000 --restart always -v /opt/registry:/var/lib/registry --name registry registry:2

## 查看本地registry 仓库所有镜像
$ curl -XGET http://10.2.2.50:5000/v2/_catalog

## 依据镜像名称查询镜像版本
$ curl -XGET http://10.2.2.50:5000/v2/nginx/tags/list

假设启动镜像仓库服务的主机地址为10.2.2.10,该目录中已存在的镜像列表(仓库镜像已分享):
链接:https://pan.baidu.com/s/19bA2f0aUjYzpZDQLczvYKw
提取码:rnb4

现镜像仓库地址 原镜像仓库地址
10.2.2.10:5000/coreos/flannel:v0.11.0-amd64 quay.io/coreos/flannel:v0.11.0-amd64
10.2.2.10:5000/mysql:5.7 mysql:5.7
10.2.2.10:5000/nginx:alpine nginx:alpine
10.2.2.10:5000/centos:centos7.5.1804 centos:centos7.5.1804
10.2.2.10:5000/elasticsearch/elasticsearch:7.4.2 docker.elastic.co/elasticsearch/elasticsearch:7.4.2
10.2.2.10:5000/fluentd-es-root:v1.6.2-1.0 quay.io/fluentd_elasticsearch/fluentd:v2.5.2
10.2.2.10:5000/kibana/kibana:7.4.2 docker.elastic.co/kibana/kibana:7.4.2
10.2.2.10:5000/kubernetesui/dashboard:v2.0.0-beta5 kubernetesui/dashboard:v2.0.0-beta5
10.2.2.10:5000/kubernetesui/metrics-scraper:v1.0.1 kubernetesui/metrics-scraper:v1.0.1
10.2.2.10:5000/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0 quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
10.2.2.10:5000/jenkinsci/blueocean:latest jenkinsci/blueocean:latest
10.2.2.10:5000/sonarqube:7.9-community sonarqube:7.9-community
10.2.2.10:5000/postgres:11.4 postgres:11.4

15.查看容器日志

## 查看全部日志
$ docker logs nginx

## 实时查看最新日志
$ docker logs -f nginx

## 从最新的100条开始查看
$ docker logs --tail=100 -f nginx

16.停止或者删除容器

## 停止运行中的容器
$ docker stop nginx

## 启动退出容器
$ docker start nginx

## 删除非运行中状态的容器
$ docker rm nginx

## 删除运行中的容器, -f 强制删除
$ docker rm -f nginx

17.查看容器或者镜像的明细

## 查看容器详细信息,包括容器IP地址等
$ docker inspect nginx

## 查看镜像的明细信息
$ docker inspect nginx:alpine

通过1号进程理解容器的本质

docker exec -ti my-nginx-alpine /bin/sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   30 nginx     0:00 nginx: worker process
   31 nginx     0:00 nginx: worker process
   32 nginx     0:00 nginx: worker process
   33 nginx     0:00 nginx: worker process
   34 root      0:00 /bin/sh
   39 root      0:00 ps aux

容器启动的时候可以通过命令去覆盖默认的CMD

$ docker run -d --name xxx nginx:alpine <自定义命令>
# <自定义命令>会覆盖镜像中指定的CMD指令,作为容器的1号进程启动

$ docker run -d --name test-3 nginx:alpine echo 123
123

$ docker run -d --name nginx4 nginx:alpine ping www.baidu.com

本质上讲容器是利用namespace和cgroup等技术在宿主机中创建的独立的虚拟空间,这个空间内的网络、进程、挂载等资源都是隔离的

$ docker exec -ti my-nginx /bin/sh
#/ ip addr
#/ ls -l /
#/ apt install xxx
#/ #安装的软件对宿主机和其他容器没有任何影响,和虚拟机不同的是,容器间共享一个内核,所以容器内没法升级内核
posted @ 2020-09-10 23:33  一墨无辰  阅读(397)  评论(0)    收藏  举报