Docker

Docker

安装

  • DaoCloud 下载并安装(推荐,速度快)

    • 安装 docker
      curl -sSL https://get.daocloud.io/docker | sh
      
    • 安装 docker-compose
      curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
      chmod +x /usr/local/bin/docker-compose
      
    • 配置 docker 镜像站
      curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
      
  • Debian/Ubuntu 环境安装 docker 社区版

    1. 新增 docker apt repository

      # curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
      # sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
      
      curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
      sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
      
    2. 安装 docker-ce
      sudo apt-get update
      sudo apt-get install docker-ce

      非官方源是不可信任的,可能会出现异常 “W: GPG error: https://download.docker.com trusty Release: The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8”,解决办法是导入该源的公钥
      gpg --keyserver download.docker.com --recv 7EA0A9C3F273FCD8 && gpg --export --armor 7EA0A9C3F273FCD8 | sudo apt-key add -

      curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
      sudo apt-get update

      启动 docker 服务
      service docker start
      停止 docker 服务
      service docker stop

  • ubuntu 14.04 离线安装 docker 和 docker compose

  • 使用 registry 搭建 docker 私有 hub

    1. 下载 registry 镜像
      docker pull registry

    2. 创建一个 registry 的容器并运行(”-p 5000:5000” 表示将容器内部的 5000 端口映射到宿主机的 5000 端口)

      docker run -d -p 5000:5000 registry
      
      docker run \
          -d \ # 作为 daemon 进程启动,即后台启动
          -v /myrepo:/var/lib/registry \ # 将容器 /var/lib/registry 目录映射到宿主机的 /myrepo,用于存放镜像数据
          -p 5000:5000 \ # 将容器的 5000 端口映射到 Host 的 5000 端口(5000 是 registry 服务端口)
          registry:latest
      
    3. 验证 registry 容器是否启动成功
      curl http://127.0.0.1:5000/v2/_catalog

    4. 使用 registry 管理仓库和镜像

      • 上传镜像
        1. 使用 tag 命令让本地镜像 hello-world 指向到 registry 仓库中
          docker tag hello-world localhost:5000/hello-world:latest
        2. 推送到 registry 仓库中
          docker push localhost:5000/hello-world
      • 下载镜像
        docker pull localhost:5000/hello-world
    5. 开启 TLS 认证

      1. 启动服务

        # 生成证书
        openssl req -newkey rsa:4096 -nodes -sha256 -keyout myhub.key -subj "/CN=myhub.com" -x509 -days 365 -out myhub.crt
        
        # 生成账号密码 zhangsan/123456
        docker run --rm --entrypoint htpasswd httpd:2 -Bbn zhangsan 123456 > htpasswd
        
        # 启动 registry 容器
        docker run -d -p 5000:5000 --restart=always --name registry \
            # -v /myrepo:/var/lib/registry \
            -v $(pwd):/certs \ # 将证书和私钥文件挂载进容器内 /certs 目录下
            -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/myhub.crt \ # 指定(容器内的)证书和私钥文件路径
            -e REGISTRY_HTTP_TLS_KEY=/certs/myhub.key \
            -v $(pwd):/auth \ # 将账号密码文件挂载进容器内 /auth 目录下
            -e "REGISTRY_AUTH=htpasswd" \
            -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
            -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ # 指定(容器内的)账号密码文件路径
            registry
        
      2. 配置访问

        # 配置 host
        echo "127.0.0.1 myhub.com" >> /etc/hosts
        
        # 验证服务是否启动
        curl --cacert myhub.crt https://myhub.com:5000/v2/_catalog
        
        # 下发证书
        mkdir -p /etc/docker/certs.d/myhub.com\:5000
        cp myhub.crt /etc/docker/certs.d/myhub.com\:5000
        
        # 账号密码登录
        docker login myhub.com:5000
        # username: zhangsan
        # password: 123456
        
        # 上传镜像
        docker tag hello-world myhub.com:5000/hello-world:latest
        docker push myhub.com:5000/hello-world
        
        # 下载镜像
        docker pull myhub.com:5000/hello-world
        
        # 退出登录
        docker logout myhub.com:5000
        
  • 使用 Harbor 搭建 docker 私有 hub

    1. 搭建
    2. 配置域名访问,如 https://www.mydockerhub.com
    3. 上传镜像
      1. 在 /etc/docker/certs.d 目录下配置 Harbor 服务的公钥证书
        cd /etc/docker/certs.d
        
        mkdir www.mydockerhub.com
        
        cd www.mydockerhub.com
        
        openssl s_client -showcerts -connect www.mydockerhub.com:443 < /dev/null 2> /dev/null | openssl x509 -outform PEM > www.mydockerhub.com.crt
        
      2. 登录 Harbor 服务
        docker login www.mydockerhub.com
      3. 推送镜像
        docker push www.mydockerhub.com/hello-world:latest
  • 配置阿里云镜像加速器

    1. 修改 daemon 配置文件 /etc/docker/daemon.json 来使用加速器(其中 “https://0cx54rhx.mirror.aliyuncs.com" 为已申请的专属加速器地址)

      tee /etc/docker/daemon.json <<-'EOF'
      {
          "registry-mirrors": ["https://0cx54rhx.mirror.aliyuncs.com"]
      }
      EOF
      
    2. 重启 docker 服务

      # ubuntu
      service docker restart
      
      # centos
      # systemctl restart docker
      
      # 如果是在容器中(如 docker:dind)重启 docker 服务,则直接在宿主机上执行 docker restart 重启容器即可
      
  • 开启 docker 实验功能

    1. 修改配置文件 /etc/docker/daemon.json,在 / 节点下新增配置项
      "experimental": true
      
    2. 重启 docker 服务

使用

  • 查看 docker 版本信息
    docker version
    查看 docker 系统的信息
    docker info

  • 镜像(Image)管理

    # 列出远程仓库 Docker Hub 中所有名称为 ubuntu 的镜像
    docker search ubuntu
    # 从远程仓库 Docker Hub 中下载名称为 ubuntu 的镜像到本地
    docker pull ubuntu
    # 推送/发布镜像至远程仓库 Docker Hub 中
    docker push new_image_name
    
    # 列出本地仓库中所有的 docker 镜像
    docker images
    # 删除本地仓库中名称为 ubuntu 的镜像
    docker rmi ubuntu
    # # 删除本地仓库中所有镜像
    # docker rmi $(docker images -q)
    
    # 生成一个镜像 ubuntu 的实例(即容器),并执行该容器中的 /bin/bash,从而交互式进入到该容器中(键入 Ctrl + P + Q 键退出容器但不停止,键入 Ctrl + D 键或执行命令 exit 即可退出容器且停止容器)
    docker run -it ubuntu /bin/bash
    # # 创建一个镜像 ubuntu 的容器,新建的容器处于停止状态,可以使用 docker start 命令启动
    # docker create -it ubuntu
    # # 生成一个新的容器,并运行命令 ping www.google.com
    # docker run ubuntu ping www.google.com
    # # 通过 “-v” 参数可以支持把一个宿主机上的目录挂载到容器中
    # docker run -it -v /home/downloads:/usr/downloads ubuntu /bin/bash
    # 使用 "--entrypoint bash" 覆盖容器中 entrypoint 的默认启动命令,可用于启动因原 entrypoint 命令存在问题而导致无法启动容器的镜像
    # docker run --entrypoint bash -it nginx
    # 创建一个 redis 的只读容器
    # docker run -- read-only redis
    
    # 根据当前目录下的文件 Dockerfile 构建出一个名称为 IMAGE[:VERSION] 的镜像
    docker build -t IMAGE[:VERSION] .
    docker build --squash -t IMAGE[:VERSION] . # 参数 "--squash" 表示压缩镜像,需要开启 docker 实验功能
    
    # # 查看指定镜像的创建历史
    # docker history IMAGE[:VERSION]
    
    # 打包镜像 ubuntu,并导出到文件 ubuntu.tar 中
    docker save -o ubuntu.tar ubuntu
    # 从文件 ubuntu.ta 中导入镜像到本地仓库中
    docker load -i ubuntu.tar
    # docker load < ubuntu.tar
    
  • 容器(Container)管理

    # 列出本地所有正在运行的容器
    docker ps
    # 列出本地所有的容器
    docker ps -a
    # # 列出本地所有的容器和体积大小
    # docker ps -as
    
    # 登录一个正在执行的容器
    docker attach [CONTAINER ID|NAMES]
    
    # 启动容器 # 其中 CONTAINER ID 为容器 ID、NAMES 为容器名称,可由 docker ps 查得。如启动 ID 为 5bf9b7645ed2 的容器:docker start 5bf9b7645ed2
    docker start [CONTAINER ID|NAMES]
    # 停止容器 # 如停止 NAMES 为 focused_bardeen 的容器:docker stop focused_bardeen
    docker stop [CONTAINER ID|NAMES]
    # 杀死容器
    docker kill [CONTAINER ID|NAMES]
    # docker kill $(docker ps -a -q) # 杀死所有正在运行的容器
    # 重启容器
    docker restart [CONTAINER ID|NAMES]
    # 删除容器
    docker rm [CONTAINER ID|NAMES]
    # docker rm $(docker ps -a -q) # 删除所有已经停止的容器
    
    # 从一个容器中取日志
    docker logs [CONTAINER ID|NAMES]
    
    # 列出一个容器里面被改变的文件或者目录,list 列表会显示出三种事件,A 增加的,D 删除的,C 被改变的
    docker diff [CONTAINER ID|NAMES]
    
    # 显示一个运行的容器里面的进程信息
    docker top [CONTAINER ID|NAMES]
    
    # 从容器里面拷贝文件/目录到本地一个路径
    # docker cp [CONTAINER ID|NAMES]:/container_path to_path
    docker cp /sbin/ifconfig 5bf9b7645ed2:/sbin
    
    # 使用已启动的容器,运行命令 ping www.google.com
    docker exec 5bf9b7645ed2 ping www.google.com
    # 使用已启动的容器,交互运行 /bin/sh
    docker exec -it b7542f84a6c3 /bin/sh
    
    # 导出容器到文件 /tmp/container01.tar 中
    docker export [CONTAINER ID|NAMES] > /tmp/container01.tar
    # 从文件 /tmp/container01.tar 中导入容器,并设置其 NAMES 为 focused_bardeen
    cat /tmp/container01.tar | docker import - focused_bardeen
    # # 导出容器并重新导入到镜像中,可用于压缩镜像。ref https://blog.csdn.net/qq_36763896/article/details/53293088
    # docker export <container id> | docker import - <image name>
    
    # 将容器 a404c6c174a2 保存为新的镜像 hello,并添加提交人信息和说明信息
    docker commit -a "author" -m "describe" a404c6c174a2  hello:v1
    # docker commit --change='ENTRYPOINT ["entrypoint.sh"]' a404c6c174a2  hello:v1
    # docker commit -c 'ENV JAVA_HOME=/opt/jdk' -c 'ENV PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$JAVA_HOME/bin' -c 'ENTRYPOINT service ssh start && /bin/bash' a404c6c174a2 ubuntu-jdk-ssh
    
    # 查看容器端口映射
    docker port a404c6c174a2
    docker port a404c6c174a2 8080
    
  • 网络管理

    # 创建一个 overlay 网络 overlay1
    docker network create -d overlay --attachable overlay1
    
    # 查看网络 overlay1 的信息
    network inspect overlay1
    
    # 查看 NAT 表中的 PREROUTING 链(iptables 将满足条件的数据都转发到了 DOCKER 链上去了)
    iptables -t nat --list-rules PREROUTING
    
    # 查看 NAT 表中的 DOCKER 链
    iptables -t nat --list-rules DOCKER
    
    # 查看容器的 ip
    docker inspect <容器id> | grep '"IPAddress":' | grep -o -E [0-9.]+ | head -n 1
    
    # 端口映射
    iptables -t nat -A DOCKER -p tcp --dport <容器外部端口> -j DNAT --to-destination <容器ip>:<容器内部端口>
    # iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source <容器ip> --destination <容器ip> --dport <容器内部端口>
    iptables -A DOCKER -j ACCEPT -p tcp --destination <容器ip> --dport <容器内部端口>
    
    # 取消端口映射规则
    iptables -t nat -D DOCKER -p tcp -d 0/0 --dport <容器外部端口> -j DNAT --to-destination <容器ip>:<容器内部端口>
    iptables -D DOCKER -j ACCEPT -p tcp --destination <容器ip> --dport <容器内部端口>
    
  • 删除那些已停止的容器、dangling 镜像、未被容器引用的 network 和构建过程中的 cache
    docker system prune
    强制清理
    docker system prune --all --force --volumes

  • 资源限制

    docker run --rm -it \
        --cpu-period=100000 --cpu-quota=20000 \ # 每 100 毫秒(即 100000 微秒)内,运行进程使用的 cpu 时间最多为 20 毫秒
        --cpuset-cpus="1" \ # 指定运行容器编号为 1 的 cpu。https://m.jb51.net/article/135395.htm?from=singlemessage
        -m 50M --memory-swap 50M \ # 容器可以使用 50M 的物理内存,且不能使用 swap。https://www.cnblogs.com/sparkdev/p/8032330.html
        --device-write-bps /dev/sda:30MB \ # 限制容器写 /dev/sda 的速率为 30MB/s。http://m.hangge.com/news/cache/detail_2413.html
        u-stress:latest /bin/bash
    
    stress -c 4 # 启动压力测试
    
  • 示例

    # 创建一个(虚拟的)网络接口名称为 docker1、网络 NAME 为 bridge1、子网网段为 10.0.1.* 的网桥
    docker network create -o "com.docker.network.bridge.name"="docker1" --subnet=10.0.1.0/24 bridge1
    
    # 停用网络接口 docker1
    # ifconfig docker1 down
    #apt-get install bridge-utils
    #brctl delbr docker1
    
    # 创建一个镜像为 ubuntu、网桥为 bridge1、ip 地址为 10.0.1.2、名称为 client1 的容器,并将宿主机目录 /sbin 挂载到容器中目录 /sbin 下,交互式启动容器中 /bin/bash
    docker run -it --network bridge1 --ip 10.0.1.2 --name client1 -v /sbin:/sbin ubuntu /bin/bash
    
  • 构建镜像

    • 构建极简镜像(镜像中应用存在依赖关系)
      例如,基于一个空的基础镜像完成一个 /bin/hello 的镜像

      1. 查看 /bin/hello 的依赖库,并将 /bin/hello 以及其依赖的动态库,打包至文件 rootfs.tar.gz 中
        ldd /bin/hello | perl -p -e 's#^\s*(\S+ => )?(/\S+) \(0x[0-9a-z]+\)$#$2#g' | grep -v 'linux-vdso' | sed '$a/bin/hello' | xargs tar zcvf rootfs.tar.gz

      2. 编辑文件 Dockerfile 构建镜像

        FROM scratch 
        
        ADD rootfs.tar.gz / 
        
        # COPY redis.conf /etc/redis/redis.conf 
        # EXPOSE 6379
        
        # CMD ["redis-server"]
        CMD ["/bin/hello"]
        
      3. 构建镜像 hello
        docker build -t hello .

    • 构建简单镜像(镜像中应用无依赖)

      1. 编辑文件 Dockerfile

        FROM scratch
        ADD hello /
        CMD ["/hello"]
        

        Docker registry 中,有一个被称为 scratch 的使用空 tar 文件构建的特殊镜像
        tar cv --files-from /dev/null | docker import - scratch
        基于该映像可构建新的无冗余的镜像

      2. 构建镜像 hello
        docker build -t hello .

    • 多阶段构建

      FROM gcc AS mybuildstage
      COPY hello.c .
      RUN gcc -o hello hello.c
      FROM alpine
      COPY --from=mybuildstage hello .
      CMD ["./hello"]