Go To My HomePage

docker基础篇

一、简介

​ Docker是PASS【Platform As A Service】提供商dotCloud开源的一个基于LXCLinux Container】的高级容器引擎,源代码托管在Github上。基于go语言并遵从Apache2.0协议开源

​ Docker的主要目标是“Build and Run Any App, Anywhere“,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP及其运行环境能够做到“一次封装,到处运行”

传统的开发部署协作方式

传统部署

Docker的开发部署协作方式

docker部署

二、虚拟化技术

虚拟机技术

虚拟机

​ 虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器。管理程序允许多个 VM 在一台机器上运行。每个 VM 都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此占用大量空间。而且 VM 启动也十分缓慢。

容器虚拟化技术

容器

​ 容器是一个应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行。与虚拟机相比,容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动。

三、Docker三要素

镜像(Image)

​ 镜像就是一个只读模板。镜像可以用来创建Docker容器,一个镜像可以创建多个容器。

容器(Container)

​ Docker利用容器独立运行一个或一组应用。容器是镜像创建的运行示例。它可以被启动、停止、删除。每个容器都是相互隔离的、保证安全的平台。

仓库(Repository)

​ 仓库是集中存放镜像文件的场所。仓库和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库又包含了多个镜像,每个镜像又不同的标签(tag)。

注:Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包形成一个可交付的运行环境,这个打包好的运行环境就是镜像文件。只有通过这个镜像文件才能生成Docker容器。镜像文件可以看作是容器的模板。Docker根据镜像文件生成容器的实例。同一个镜像文件,可以生成多个同时运行的容器实例。

四、YUM安装Docker

第一步:卸载原Docker组件,确认安装了gcc、gcc-c++

#卸载旧docker组件
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 -y install gcc gcc-c++

第二步:安装Docker仓库

#安装必要的一些系统工具
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

第三步:更新、安装、开启Docker服务

#更新并安装Docker-CE
yum makecache fast
yum -y install docker-ce

#开启Docker服务
systemctl start docker

第四步:配置镜像加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["自己的阿里云加速器地址"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

第五步:运行hello-world镜像验证是否安装成功

docker run hello-world

五、命令

帮助命令

  • 查看版本信息 docker version
  • 查看详细信息 docker info
  • 查看命令帮助 docker –help

镜像命令

  • 列出镜像 docker images

    参数 描述
    -a 列出本地主机上的镜像(包括中间层镜像)
    -q 列出本地主机上的镜像ID
  • 搜索镜像

    docker search [镜像名]

    参数 描述
    –filter=stars=3 列出点赞数不小于指定值的镜像
    –no-trunc 显示完整镜像描述
  • 拉取/删除镜像

    docker pull/rmi [镜像名]:[Tag]

    小技巧:docker rmi -f $(docker images -qa) 删除所有镜像

  • 提交容器作为镜像

    docker -a=”作者名” -m=”描述信息” commit [容器ID] 名称:Tag标签

  • 保存【导出】镜像

    docker save [镜像名]:[Tag] -o /path/[name].tar

  • 加载镜像

    docker load -i [name].tar

容器命令

  • 运行容器命令 docker run [镜像名]:[Tag]

    参数 描述
    -i 以交互模式运行容器,通常与-t同时使用
    -t 为容器重新分配一个伪输入终端,通常与-i同时使用
    -p 指定映射端口,形式hostPort:ContainerPort
    -P 随机端口映射
    -d 后台运行容器
    –name 为容器指定名称
    –restart=always docker服务启动容器就启动
    -h name 设置主机名
    –add-host name:ip 注入hostname的IP解析
    –rm 容器停止时自动删除
    –link cname:hostName 向容器的/etc/hosts添加另一个IP地址的映射形式
  • 退出容器方法

    • exit 容器停止退出
    • ctrl+p+q 容器不停止退出
  • 查看容器进程 docker ps

    参数 描述
    -l 上次运行的容器
    -a 所有运行过的容器
    -q 静默只显示容器编号
  • 启动/重启/停止容器

    docker start/restart/stop [容器ID]

  • 强制停止容器

    docker kill [容器ID]

  • 删除关闭的容器

    docker rm [容器ID]

    参数 描述
    -f 强制删除

    小技巧:docker rm -f $(docker ps -qa) 删除所有容器

  • 查看容器日志 docker logs [容器ID]

    参数 描述
    -f 一直追加,显示最新日志
    -t 显示每条日志时间戳
  • 查看容器运行进程信息

    docker top [容器ID]

  • 查看容器内部细节

    docker inspect [容器ID]

  • 查看容器占用的系统资源

    docker stats [容器ID]

    不加容器ID则查看所有容器的系统资源

  • 重新进入容器

    docker attach [容器ID]

  • 进入容器执行Shell命令

    docker exec -it [容器id Shell命令]

  • 拷贝容器内文件

    docker cp [容器ID]:容器路径 本机路径

  • 容器运行机制

​ Docker容器后台运行,就必须有一个前台进程,容器运行的命令如果不是会挂起的命令(比如top),会自动退出。最佳的解决方案是,将要运行的程序以前台进程的形式运行

六、Docker Compose

​ Docker Compose是容器编排工具,允许用户在一个模板(YAML格式)中定义一组相关联的容器,对启动的优先级进行排序。

命令 解释
-f 指定YAML文件位置
ps 显示容器所有信息
start/stop/restart 启动/停止/重启容器【已加载成为容器】
logs 查看日志信息
config -q 验证YAML是否正确
up -d 启动容器项目【未加载成为容器】
pause 暂停容器
unpause 恢复暂停
rm 删除容器

简单示例:

version: '3'
services: 
  db: 
    image: 'mysql:5.7'
    restart: 'always'
    environment: 
      MYSQL_ROOT_PASSWORD: 'root'
      MYSQL_DATABASE: 'wordpress'
      MYSQL_USER: 'wordpress'
      MYSQL_PASSWORD: 'wordpress'
  wordpress: 
    depends_on:
      - db
    image: 'wordpress:latest'
    restart: 'always'
    ports: 
      - '8080:80'
    environment: 
      WORDPRESS_DB_HOST: 'db:3306'
      WORDPRESS_DB_USER: 'wordpress'
      WORDPRESS_DB_PASSWORD: 'wordpress'

七、镜像加载原理

UnionFS 联合文件系统

​ 联合文件系统是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层一层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分成来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

​ 特征:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker镜像加载原理

​ docker的镜像实际上由一层一层的文件系统组成即联合文件系统。

docker镜像原理

​ bootfs(boot file system)主要包含bootloader和kernel, bootloader 主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在 Docker镜像的最底层是bootfs.这一层与我们典型的Linux/Unix系统是一一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权己由bootfs转交给内核,此时系统也会卸载bootfs。

​ rootfs (root file system),在bootfs之 上.包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是 各种不同的操作系统发行版,比如Ubuntu, Centos等等 。

​ 对于一个精简的OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的kernel,自己只需要提供rootfs就行了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs 。

docker镜像结构

​ 注意:docker在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。

八、容器数据卷

简介

​ 容器数据卷的作用是数据共享和数据持久化。卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过联合文件系统,提供一些用于持续存储或共享数据的功能。

​ 卷的设计目的就是持久化,完全独立于容器的生命周期,因此docker不会在容器删除时删除其挂载的数据卷。如果没有指定映射卷位置(容器数据卷)将会在/var/lib/docker/volumes下创建默认容器管理数据卷

容器卷具有以下特点:

  • 容器卷可在容器之间共享或重用数据
  • 卷中的更改可以直接生效
  • 数据卷中的更改不会包含在镜像的更新当中
  • 数据卷的生命周期一直持续到没有容器使用它为止

使用容器数据卷

挂载完成可以使用docker inspect命令查看相关数据卷挂载信息

  • 使用命令

    docker run [options] -v [宿主机绝对路径]:[docker容器绝对路径] 镜像ID 挂载容器数据卷

    docker run [options] -v [宿主机绝对路径]:[docker容器绝对路径]:[ro] 镜像ID 只读模式挂载

    指定宿主机路径,目的时为了读写数据

    若不指定宿主机路径,则docker自动创建一个目录,这样使用是为了共享数据

    注意:当宿主机删除与容器挂载的目录时,容器的挂载目录变为只读。即使宿主机恢复容器的挂载目录也无效

  • 使用DockerFile文件的VOLUME命令创建容器管理数据卷

    VOLUME [“docker容器绝对路径”,”docker容器绝对路径”]

    只会在容器内创建,宿主机目录由docker自行创建,目的时为了数据共享

  • 数据卷容器

    命名的容器挂载数据卷,其它容器通过挂在这个父容器实现数据共享,挂载的容器被称为数据卷容器

    docker run [options] –volumes-from 数据卷容器名称 镜像名称

九、DockerFile

基础知识

  • 每条保留字指令都必须为大写字母且后面至少跟随一个参数
  • 指令按照从上到下,顺序执行
  • #表示注释
  • 每条指令都会创建一个新的镜像层,并对镜像进行提交
  • 最大不能超过128层

保留关键字

关键字 描述
FROM 基础镜像,当前新镜像时基于哪个镜像
LABEL 镜像维护者的信息
RUN 执行的SHELL命令
EXPOSE 暴露服务的端口,只起显示作用
WORKDIR 终端登陆之后的工作目录
ENV 设置环境变量
ADD 把文件拷贝到镜像并解压【必须和Dockerfile在同一级目录下,不用加绝对路径】
COPY 把文件拷贝到镜像【必须和Dockerfile在同一级目录下,不用加绝对路径】
VOLUME 创建数据容器卷
CMD 指定容器启动时要运行的命令,可以有多个CMD命令,但只有最后一个生效,CMD会被docker run 之后的参数替换
ENTRYPOINT 指定容器启动时要运行的命令,不会覆盖只会追加
ONBUILD 当构建一个被继承的Dockerfile时运行的命令,父镜像在被子镜像继承时触发

示例

  • 简单的使用
#在centos上架构
FROM centos
#作者信息
LABEL org.kun.vendor="WangYuKun" org.kun.build-date="20181218"
#设置环境变量,会存在于容器环境变量当中
ENV USERLOCAL /usr/local
#安装vim
RUN yum install -y vim
#设置登陆终端时的交互目录
WORKDIR ${USERLOCAL}
#启动执行的命令
CMD ["/bin/bash"]
  • CMD和ENTRYPOINT
#在centos上架构
FROM centos
#安装curl
RUN yum install -y curl
#执行时可以在后追加参数,例如docker run [镜像名] -i 相当于 curl -s https://ip.cn
ENTRYPOINT ["curl","-s","https://ip.cn"]

#执行不可以追加参数相当于覆盖操作
#CMD ["curl","-s","https://ip.cn"]
  • ONBUILD
FROM centos
RUN yum install -y curl
#在子类构建镜像时会触发,注意ONBUILD仅仅时触发器,后面需要跟保留关键字起作用,不能直接使用命令
ONBUILD RUN ["echo","onbuild tigger"]
  • 自定义tomcat
FROM centos
#拷贝jdk压缩包并解压
ADD jdk-8u191-linux-x64.tar.gz /opt/
RUN mv /opt/jdk1.8.0_191 /opt/jdk8
#配置java环境变量
ENV PATH=$PATH:/opt/jdk8/bin
ENV JAVA_HOME=/opt/jdk8

#拷贝tomcat压缩包并解压
ADD apache-tomcat-8.5.35.tar.gz /opt/
RUN mv /opt/apache-tomcat-8.5.35 /opt/tomcat
ENV CATALINA_HOME=/opt/tomcat
#设置工作目录
WORKDIR $CATALINA_HOME
#设置要暴露的端口
EXPOSE 8080

#启动tomcat并打印日志
CMD /opt/tomcat/bin/startup.sh start && tail -f /opt/tomcat/logs/catalina.out

十、镜像仓库搭建

官方仓库搭建方式

仓库端

  • 启动仓库
docker run -d -v /data/registry:/var/lib/registry -p 5000:5000 --restart=always registry
  • 修改【 /etc/docker/deamon.json】文件
{
  "insecure-registries":["仓库端IP:5000"]
}
  • 重启docker服务
systemctl restart docker

客户端

  • 修改【 /etc/docker/deamon.json】文件
{
  "insecure-registries":["仓库端IP:5000"]
}
  • 重启docker服务
systemctl restart docker
  • 查看仓库镜像
# 查看镜像
curl -XGET http://registrIP:5000/v2/_catalog
# 查看具体镜像版本
curl -XGET http://registrIP:5000/v2/image_name/tags/list
  • 打包上传镜像
# 命令格式docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[/libraryName][:TAG]
# 指定仓库需要加libraryName,官方搭建方式不能指定仓库,harbor可以创建仓库
# 例如:docker tag hello-world:latest 192.169.1.149:5000/hello-world:v1.0
docker tag SOURCE_IMAGE:[TAG] registrIP:5000[/userName]/TARGET_IMAGE:[TAG] 

# 上传镜像到仓库
# 私有仓库登陆才能推送
# docker login ip/hostname
# 例如:docker push 192.168.1.149:5000/hello-world:v1.0
docker push registrIP:5000[/userName]/TARGET_IMAGE:[TAG]

Harbor仓库搭建

Harbor构建需要先安装python、compose。harborGithub地址

  • 构建SSL证书
# 构建证书
openssl genrsa -des3 -out server.key 2048
openssl req -new -key server.key -out server.csr

# 脱密,证书退密钥
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key

# 证书有效期
openssl x509 -req -days 365 -in server.csr -signkey server.key -out  server.csr

# 将证书移置指定目录
mkdir /data/cert
chown -R 777 /data/cert
cp server.*  /data/cert
  • 修改harbor.yml
# 修改hostname项
hostname: [hostname]

# 屏蔽HTTP协议
#http:
#  port: 80

# 开放https协议
https:
  port: 443
  certificate: /data/cert/server.csr
  private_key: /data/cert/server.key
  • 执行安装脚本
./install.sh
  • 访问harbor https://ip/harbor/projects ,默认账户:admin,密码:Harbor12345

客户端配置(非进行公网认证过的SSL证书)

  • 修改【 /etc/docker/deamon.json】文件
{ 
 "insecure-registries":["registryIP"]
}
  • 重启docker服务
systemctl restart docker

打包示例

docker tag hello-world:latest www.kun.com/library/hello-world:v1.0

推送示例

先登录:docker login www.kun.com

docker push www.kun.com/library/hello-world:v1.0

十一、网络设置

网络通讯

Docker-net

  • 容器与容器之间通讯使用docker的网桥(类似交换机作用)进行相互之间铜须

  • 容器访问外部网络使用SNAT转换

  • 外部网络访问容器使用DNAT转换

网络模式修改

进程网络修改【不常用】

  • -b,–bridge=”“:指定Docker使用的网桥设备,默认情况下Docker会自动创建和使用docker0网桥设备,通过此参数可以设置已经存在的设备

  • –bip:指定Docker0的IP和掩码,使用标准的CIDR格式如10.10.10.10/24

  • –dns:配置容器的DNS,在启动Docker进程时添加,所有容器全部生效

容器网络修改

  • –dns:配置容器的DNS
  • –net:指定容器的网络通讯方式
    • bridge:Docker默认方式,网桥模式
    • none:容器没有网络栈
    • container:使用其它容器的网络栈,容器会加入其它容器的network namespace
    • host:表示容器使用Host的网络,没有自己独立的网络栈,容器可以完全访问Host的网络,不安全

端口暴露方式

p/P 选项的使用格式

  • -p : 将制定的容器端口映射至主机所有地址的一个动态端口
  • -p : 映射至指定的主机端口
  • -p :: 映射至指定的主机的IP的动态端口
  • -p :: 映射至指定的主机IP的主机端口
  • -P(大写) 暴露所需要的所有端口(expose端口)到随机端口

docker port [ontainerName] 可以查看容器当前的映射关系

容器间网络隔离

第一步:创建独立network namespace

docker network create -d bridge \
[--subnet "172.26.10.0/16"] \
[--gateway "172.26.10.1"] \
[netName]

第二步:运行镜像是指定network

docker run -d --network=[netName] [imageName]

查看namespace: docker network ls

十二、内存&CPU限制

​ 默认情况下Docker创建的容器会尽可能是用完操作系统内存,当服务异常时可能会导致崩溃,所以生产环境下CPU和内存必须加以限制。Dockers底层使用CGroup进行资源限制。

Linux内核有OOME机制【Out Of Memory Exception】

​ 一旦发生OOME,任何进程都有可能被杀死,包括docker daemon在内,为此docker调整了docker daemon的OOM优先级,以免被内核关闭。

内存相关设置

  • -m,–memory 容器能使用的最大内存大小,单位M

  • –memory-swap 容器可使用的swap大小

    • –memory为正数M,–memory-swap为正数S,swap空间为(S-M)
    • –memory为正数M,–memory-swap为0或者unset,容器可用swap为2*M
    • –memory为正数M,–memory-swap为-1,使用主机的swap限制

    容器内使用free看到的swap空间不具有真实含义

  • –memory-swappiness swap出内存的比例。取值0-100,0代表不使用swap
  • –memory-resercation 内存软限制
  • –oom-kill-disable 发生OOME时是否杀死容器,只有配置-m时使用才安全,否则会造成内存耗尽

CPU相关设置

​ Docker提供的CPU资源限制选项可以在多核系统上限制容器能利用哪些vCPU,而对容器最多能使用的CPU时间 有两种限制方式:①设置各个容器能使用的CPU时间相对比例。②以绝对的方式设置容器在每个调度周期内最多能使用的时间

  • –cpuset-cpus=”” 使用的CPU集,例如:”0,1,1-3”分别表示使用第一块、第二和第一、第二、第三块CPU
  • -c,–cpu-shares=1024 CPU使用权重,默认1024
  • –cpu-period=0 一个调度周期时间单位微秒(1000~1000000),和cpu-quota联用
  • –cpu-quota=0 一个调度周期使用的CPU时间单位微妙,和cpu-period联用

docker run -it –cpu-period=10000 –cpu-quota=20000 centos /bin/bash #代表使用两块CPU

十三、Docker安装MySQL、Redis

  • 安装MySQL
#拉取MySQL
docker pull mysql:5.7

#注意外部访问需要修改root权限,docker mysql配置文件再/etc/mysql目录下,注意查看内容
docker run -p3306:3306 \
-v /root/mysql/log/mysqld.log:/logs \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/conf/my.cnf:/etc/mysql/my.conf \
-e MYSQL_ROOT_PASSWORD=123456  \
-d mysql:5.7
  • 安装Redis
#拉取redis
docker pull redis

#启动redis,注意redis.conf中的dir要有权限,bind注释掉,protected-mode改为no
docker run -p6379:6379 \
-v /root/redis/conf/redis.conf:/usr/local/etc/redis.conf \
-v /root/redis/data:/data \
--privileged=true 
-it redis redis-server /usr/local/etc/redis.conf

附名词解释&容器状态转换图

IASS&PASS&SASS

传统部署

LXC

​ Linux的container技术在内核层面由两个独立的机制保证,一个保证资源的隔离性,名为namespace,一个进行资源的控制,名为cgroup。

namespace

​ namespace是对全局系统资源的一种封装隔离,使得处于不同namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其他namespace中的进程没有影响。

​ Linux现有的namespace有6种: uts[主机名], pid, ipc[进程通信], mnt[挂载点], net和user

cgroup

​ cgroup是Linux下的一种将进程按组进行管理的机制,在用户层看来,cgroup技术就是把系统中的所有进程组织成一颗一颗独立的树,每棵树都包含系统的所有进程,树的每个节点是一个进程组,而每颗树又和一个或者多个subsystem关联,树的作用是将进程分组,而subsystem的作用就是对这些组进行操作。

subsystem

​ 一个subsystem就是一个内核模块,他被关联到一颗cgroup树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem经常被称作”resource controller”,因为它主要被用来调度或者限制每个进程组的资源【比如限制CPU的使用时间,限制使用的内存,统计CPU的使用情况,冻结和恢复一组进程等】

hierarchy

​ 一个hierarchy可以理解为一棵cgroup树,树的每个节点就是一个进程组,每棵树都会与零到多个subsystem关联。在一颗树里面,会包含Linux系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗cgroup树,每棵树都和不同的subsystem关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的subsystem关联。

容器状态转换图

container-status-convert