美文网首页
Docker基础以及实践

Docker基础以及实践

作者: 明翼 | 来源:发表于2021-09-17 23:04 被阅读0次
    Docker

    我使用docker,也只是简单的使用,主要用来快速搭建环境,比如快速搭建Elasticsearch环境,进行各种验证,逐渐发现docker用起来很爽,真如docker含义一样“码头工人”,也类似于docker的图标,docker把应用容器化了,给人一种干净方便的感觉,使用过程也是如此,只需要简单的几个命令就可以快速构建一个完全一样的环境,不用了,直接删除,干干净净,不用做过多复杂的配置,不用费力地改各种配置文件,清理后也不用担心还遗留的垃圾,所以学习下docker还是很有必要。

    有些docker命令我也记不得了,这篇文章算是我的笔记,边学习边写,主要分为三个部分:

    1. docker基础和原理; 2. docker 常用命令; 3.docker 实战

    一 docker 基础和原理

    1.1 容器

    聊docker 就会谈到容器即container的概念,container有集装箱的含义,集装箱有什么作用那:
    首先可以很好的隔离货物,货物之间不受到影响;其次可以限制每种货物的边界,使得货物可以方便的装载和卸载;集装箱可以反复使用;

    这些特性在容器中刚好也存在,我们通过容器来隔离一个个应用,也许你会说了,在OS中,各个应用本来就相互独立的啊,一个程序挂掉并不会影响另外一个程序啊,其实虽然OS中,大部分情况可以减少应用之间的相互影响,但是使用的资源还是统一的,比如都可以使用系统的内存,如果一个程序占用内存很大,比然会影响其他应用的执行。 有了影响,就会造成了整个系统的不稳定。

    所以有了各种隔离的技术,有虚拟机,也有今天说的容器。docker是容器技术的一个开源的实践。
    相对于虚拟机来说,容器的优点更轻量级,大部分几十M到上百M,比虚拟机更小,因为容器可以共享底层的系统,只是把运行环境独立开来,所以体积更小,这样我们在一个系统中可以同时启动上千个容器; 因为体积更小,底层公用,所以启动的速度几乎是秒级; 也因为底层是直接OS,所以性能比虚拟机这种需要透过两层OS的性能更好。

    虚拟机 VS 容器

    1.2 docker基础概念

    docker中最重要的三个概念,就是镜像(Image),容器(Container),和仓库(Repository)。
    简单的来理解,镜像可以看做可以执行的程序(包含执行程序所需要的环境,脚本,配置等以切程序执行所依赖的东西),这样我们可以保证镜像导出到其他环境执行结果都是完全一样的;容器那可以看做系统中的进程,即程序运行起来变成了进程;仓库是保存镜像的地方,我们可以从仓库中拉取镜像,这类似于我们的git仓库,只是一个保存的是镜像,一个保存的是源码而已。

    如果说镜像是可执行程序,一般程序要么是通过解释执行,要么是通过编译执行,这个可执行文件是怎么来的,其实这个可执行文件是通过docker file文件借助docker build工具来创建镜像,docker file文件包含基础镜像信息、 维护者信息、 镜像操作指令和容器启动时执行指令等信息

    docker概念关系

    docker 通过拉取仓库镜像,或者导入镜像,然后通过run命令运行镜像就形成了一个个容器。

    1.3 docker 简单原理

    Docker 利用Linux的核心分离机制,利用Namespace做资源隔离,用Cgroup做资源限制,利用Union FS 做容器文件系统的轻量级虚拟化技术。Docker容器本身仍然是宿主机器上的特殊进程,只是它的资源受限,它看到的进程和文件系统是隔离的,共享操作系统的内核,所以它是轻量级的虚拟化技术。

    1.3.1 docker 隔离原理

    进程隔离
    首先,我们要理解为什么要做隔离,比如我们普通的服务器上,可能跑着web容器如nginix,可能跑着数据库如mysql,可能跑着我们的spring boot程序,如果不是隔离的,每个进程都可以看到其他进程,也可以看到或访问其他所有文件,特别是用root用户启动的进程,那就存在一个问题,就是如果其中一个进程有漏洞,被黑客攻进来了,那他就可以访问当前系统的所有文件和进程,显然是没有必要而且危害大。

    Linux进程组

    在linux宿主主机上,我们可以看到很多进程,都是由idle这个0号进程直接或间接创建,进程之间没有隔离。

    Docker进程组

    进入到容器内,只能看到容器内极少进程,从而达到和宿主的主进程隔离的目的。

    Linux通过Namespace实现了资源的隔离,如下的接口:

    #include <sched.h>
    
    int clone(int (*fn)(void *), void *child_stack,
    int flags, void *arg, ...
    /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
    

    如:

    int pid = clone(call_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
    

    clone类似fork创建一个进程,新进程会执行call_function, 我们通过指定flags为CLONE_NEWPID | SIGCHLD,那么该进程的编号就为隔离状态,有就是新的PID编号,该进程内看到的只能看到自己的进程ID为1,看不到其他宿主机器的其他进程,或其他容器里面的进程。

    int unshare(int flags) 使进程脱离某个Namespace,flags参数和clone的用法一致。
    int setns(int fd, int nstype) 使进程进入某个已经存在的Namespace。经常用于从宿主机进入已经启动的容器Network Namespace,然后设置它的网络。
    

    网络隔离
    docker为我们提供四种网络连接方式:

    网络设置模式

    默认为桥接模式:


    桥接模式

    docker在桥接模式下,为所有的容器都设置了IP地址。每个容器都会创建一对虚拟网卡,一个veth开头的网卡加入docker0的网桥内,另外一个网卡eth0在容器内。网桥docker0再通过iptables和主机网卡相连,达到了通信的目的。docker会给每个容器都分配一个新的ip,这些ip以docker0的ip为默认网关。

    centos下查看网桥接口:

    #安装
    yum install bridge-utils
    
    #查看
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# brctl show
    bridge name bridge id       STP enabled interfaces
    docker0     8000.0242324f6fed   no      veth0fab93f
                                veth3a10669
                                veth58dc63c
                                veth5b0dd66
                                vethd8f2eca
    
    [root@iZwz90jb8mqajkli0ttrcbZ ~]#  iptables -t nat -L
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination         
    MASQUERADE  all  --  172.17.0.0/16        anywhere            
    MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:redis
    MASQUERADE  tcp  --  172.17.0.5           172.17.0.5           tcp dpt:cslistener
    MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:http
    MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:mysql
    MASQUERADE  tcp  --  172.17.0.6           172.17.0.6           tcp dpt:http
    
    Chain DOCKER (2 references)
    target     prot opt source               destination         
    RETURN     all  --  anywhere             anywhere            
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:redis to:172.17.0.4:6379
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:7088 to:172.17.0.5:9000
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:rcc-host to:172.17.0.3:80
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:mysql to:172.17.0.2:3306
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:monp to:172.17.0.6:80
    

    比如我们要访问如上机器的ip为127.0.0.1的redis服务端口,报文通过iptables设置转发到172.17.0.4:6379这个容器内的6379端口上,回复的时候,这个ip又转成127.0.0.1回复。
    docker通过Namespace实现了进程隔离,又通过iptables实现了docker和外网通讯。

    文件系统隔离
    Union File System即联合文件系统,具有联合挂载能力,间容器中多层内容呈现为统一的rootfs(根文件系统)。rootfs打包了整个操作系统的文件和目录。在新进程中创建隔离的挂载点,在clone函数中传入CLONE_NEWNS,这样子进程得到父进程挂载点的拷贝,通过chroot函数改变程序能够访问的文件系统的根节点,从而禁止访问宿主机器上的其他目录。

    1.3.2 资源限制

    同一机器上运行多个容器,说到底还是会使用宿主机器上的cpu和内存,如果一个容器运行IO密集型任务或CPU密集性任务,还是会影响其他进程和容器的运行。

    Control Groups(CGroups)能够隔离宿主主机上的物理资源(CPU,内存,磁盘IO,网络带宽)。


    cgroup具有层级关系

    CGroups以子系统的形式设置很多参数,将需要限制的进程加入后,就按照这些参数的设置来限制进程的使用的资源了。
    下面是cgroups所有的挂载点,限制的时候,只要在下面建立目录,自动会创建相关配置文件,然后把要配置的值写入 ,然后进程启动加入这个限制组即可对进程使用资源做限制。

    [root@iZwz90jb8mqajkli0ttrcbZ ~]# mount | grep cgroup
    tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
    cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    

    我们可以简单看下容器的限制:

    [root@iZwz90jb8mqajkli0ttrcbZ ~]# docker ps
    CONTAINER ID   IMAGE                 COMMAND                  CREATED        STATUS        PORTS                               NAMES
    6c9d39515afa   nginx                 "/docker-entrypoint.…"   1 months ago   Up 1 days     0.0.0.0:3445->80/tcp                nginx
    
    #查看这个容器的内存使用限制,很大的值,内存使用没做限制。
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# more /sys/fs/cgroup/memory/docker/6c9d39515afa7d349c16883b04ee5ccf3bcc158e8679a80c9e3e6fc90c2a057f/memory.kmem.limit_in_bytes 
    9223372036854771712
    
    docker的cgroups结构

    每个CGroup下面的tasks 记录属于这个CGrop的所有进程id,cpu.cfs_quota_us文件能对cpu做出限制,如果当前文件内容为50000,那么当前控制组的所有进程的cpu占用率不能超过50%。

    [root@iZwz90jb8mqajkli0ttrcbZ ~]#  docker run -it -d --cpu-quota=50000 busybox
    Unable to find image 'busybox:latest' locally
    latest: Pulling from library/busybox
    8ec32b265e94: Pull complete 
    Digest: sha256:b37dd066f59a4961024cf4bed74cae5e68ac26b48807292bd12198afa3ecb778
    Status: Downloaded newer image for busybox:latest
    a39c6479232bbecd8e6d57837ece46fbbe55f7e96e93b066b4e7669adf68b81e
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# docker ps
    CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                               NAMES
    a39c6479232b   busybox               "sh"                     21 seconds ago   Up 20 seconds                                       nostalgic_galileo
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# cat /sys/fs/cgroup/cpu/docker/a39c6479232bbecd8e6d57837ece46fbbe55f7e96e93b066b4e7669adf68b81e/tasks 
    1523538
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# ps -ef|grep 1523538
    root     1523538 1523521  0 20:31 ?        00:00:00 sh
    root     1523603 1474176  0 20:31 pts/1    00:00:00 grep --color=auto 1523538
    [root@iZwz90jb8mqajkli0ttrcbZ ~]# cat /sys/fs/cgroup/cpu/docker/a39c6479232bbecd8e6d57837ece46fbbe55f7e96e93b066b4e7669adf68b81e/cpu.cfs_quota_us
    50000
    

    1.3.3 image和container

    image和container

    docker的镜像是分层的,每层都是只读的,docker run启动一个镜像后,会在上面生成一个可读可写的层,如下:

    容器分层

    容器等于镜像+可读写层,如下:


    容器和镜像

    docker的构建是在docker file上构建的,docker file中每个命令都是在已有层上加上一个只读层:
    如下:

    FROM ubuntu:15.04
    COPY . /app
    RUN make /app
    CMD python /app/app.py
    

    容器可以看做在镜像层基础上添加个读写层,上述形成的容器如下:

    容器

    当镜像被 docker run 命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。

    1.4 AUFS

    UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。

    AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount):


    AUFS

    每一个镜像层或者容器层都是 /var/lib/docker/ 目录下的一个子文件夹;在 Docker 中,所有镜像层和容器层的内容都存储在 /var/lib/docker/aufs/diff/ 目录中:

    $ ls /var/lib/docker/aufs/diff/00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c       93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8
    00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c-init  93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8-init
    019a8283e2ff6fca8d0a07884c78b41662979f848190f0658813bb6a9a464a90       93b06191602b7934fafc984fbacae02911b579769d0debd89cf2a032e7f35cfa
    ...
    

    二 docker 常用命令

    docker 命令

    docker 是CS架构的系统,Docker的守护进程运行在主机上,客户端通过socket连接到守护进程,发送命令,服务器端接收命令并执行.

    2.1 docker的安装

    我是在centos下安装的,如下命令安装:

    yum install -y yum-utils
    wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo
    yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    yum install containerd.io
    yum install https://download.docker.com/linux/fedora/30/x86_64/stable/Packages/containerd.io-1.2.6-3.3.fc30.x86_64.rpm
    yum install docker-ce docker-ce-cli --nobest
    

    阿里云加速:

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://jo5me37m.mirror.aliyuncs.com"]
    }
    EOF
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

    配置docker开机启动

    # 启动docker
      systemctl start docker
    # 使docker开机启动
      systemctl enable docker
    # 停止docker服务
      systemctl stop docker
    

    docker基本信息

    #docker version 版本信息
    docker version
    #docker info docker基本信息 docker系统信息包括镜像,容器数量等,蛮详细的.
    docker info
    #docker命令帮助
    docker --help
    #子命令帮助
     docker images --help
    

    2.2 镜像命令

    docker上面安装就相当于安装os,镜像就相当于程序了,需要先下载程序才可以运行.
    查看镜像

    #查看docker的所有下载的镜像,仓库地址,标志
    docker images
    #镜像tag等.
    

    搜索仓库镜像
    docker有做好的N多个镜像,非常方便搜索下载也是我喜欢docker原因.

    docker search mysql
    

    下载镜像

    #如果不写tag默认下载的是latest
    docker pull mysql
    #等价于:
    docker pull docker.io/library/mysql:latest
    #下载指定版本
    docker pull mysql:5.7
    

    删除镜像

    docker rmi -f 镜像id/镜像名字
    docker rmi -f $(docker images -aq) # 删除所有镜像
    

    2.3 容器的命令

    我们有了镜像就可以创建容器了,命令如下:
    运行命令

    docker run [可选参数]  image
    # 参数说明
    --name="xxx" 容器名字
    -d  后台方式运行
    -it 使用交互方式运行,可以进入容器查看内容
    -p 指定容器的端口 -p 8080:8080
    -p 主机端口:容器端口
    -p ip:主机端口: 容器端口
    
    #例子:
    docker run -it centos /bin/bash
    
    
    

    列出在运行的容器

    docker ps
    #列出 当前运行和历史运行过容器
    docker ps -a
    

    退出容器

    #退出容器 如果是没干什么事情的容器,这样会停止运行
    exit
    
    #退出容器 不停止容器
    Ctrl +P+Q
    

    删除容器

    docker rm  容器id #删除指定容器  -f 强制删除运行中的容器
    docker rm -f  $(docker ps -aq) #删除所有容器
    

    启动和停止容器

    docker start 容器id
    docker restart 容器id
    docker stop 容器id
    docker kill 容器id
    
    

    后台启动命令

    docker run centos -d
    #会自动停止,因为后台没有关联的前台命令,比如运行nginx等.
    

    容器日志查看

    #查看最近10条日志
    docker logs -f -t --tail 10 容器id
    

    查看容器中进程信息

    docker top 容器id
    

    查看容器元数据信息

    #非常详细的信息
    docker inspect 容器id
    

    进入当前正在运行的容器

    #进入容器开启新的终端,可以操作
    docker  exec  -it 容器id  /bin/bash
    
    #进入容器,正在执行的终端,不会启动新的进程
    docker attach -it  容器id /bin/bash
    

    从容器中拷贝文件到主机上

    docker cp 容器id:路径 主机目录
    

    一般可以使用-v 卷的技术实现文件的自动同步.

    三 Docker 实战

    3.1 docker 安装Mysql

    docker pull mysql
    cd /opt/
    mkdir mysql_docker
    cd mysql_docker/
    echo $PWD
    #-v /opt/mysql_docker/conf:/etc/mysql/conf.d 映射本地目录到容器目录/etc/mysql/conf.d 
    #设置mysql密码为123456  run时候如果不存在这个镜像会自动拉取此镜像
    docker run --name mysqlserver -v /opt/mysql_docker/conf:/etc/mysql/conf.d -v /opt/mysql_docker/logs:/logs -v /opt/mysql_docker/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306 mysql:latest
    
    docker ps
    
    #进入mysql
    docker exec -it mysqlserver bash
    #登录
    mysql -uroot -p
    
    use mysql;
    ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
    flush privileges;
    
    # 查看日志
    docker logs -f --tail 10 a4dac74d48f7
    
    #停止mysql
    docker stop mysqlserver
    
    #启动container 
    docker restart   a4dac74d48f7
    

    3.2 安装nginx

    # 拉取nginx
    docker pull nginx
    #新建目录
    mkdir -p nginx/www nginx/logs nginx/conf
    
    #简单启动
    docker run --name nginx-test1 -p 8081:80 -d nginx
    
    #拷贝下配置文件
    docker cp 4b51399a85a4:/etc/nginx/nginx.conf ./
    
    #正式启动
    docker run -d -p 80:80 --name nginx-app-1 -v /opt/nginx/www:/usr/share/nginx/html -v /opt/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /opt/nginx/logs:/var/log/nginx nginx
    

    3.3 安装redis

    docker pull redis
    mkdir -p /opt/redis/conf ,/opt/redis/data
    docker run -p 6379:6379 --name redis-app -v /opt/redis/redis.conf:/etc/redis/redis.conf -v /opt/redis/data:/data -d redis 
    docker exec -it redis redis-cli
    

    四 参考

    [docker从入门到实践](https://yeasy.gitbook.io/docker_practice/)
    [docker原理优秀文章](https://draveness.me/docker/)
    [docker容器和镜像理解](http://dockone.io/article/783)
    [狂神讲redis 视频]([https://www.bilibili.com/video/BV1og4y1q7M4?p=22](https://www.bilibili.com/video/BV1og4y1q7M4?p=22)
    )
    
    

    相关文章

      网友评论

          本文标题:Docker基础以及实践

          本文链接:https://www.haomeiwen.com/subject/xcizwltx.html