Docker全套用法使用详解

1. Docker使用前概要:

1.1 背景:

Docker使用google的go语言实现的

Docker VS 传统虚拟化方式:
传统虚拟化技术是虚拟出一套硬件后,在上面运行一个完整的操作系统,然后在这个系统上面运行程序。
而docker容器内的程序直接运行于宿主的内核,容器是没有自己的内核的,也没有进行硬件虚拟,所以更加的轻便

Docker有三个基本概念:

  • 镜像:

    操作系统分为内核和用户空间,对于linux而言,内核启动后,会挂载root文件系统提供用户空间支持。
    而Docker镜像image就相当于一个root文件系统,比如官方的ubuntu18镜像就包含了一整套ubuntu18
    最小系统的root文件系统。

    镜像构建的时候,是一层一层构建的,可以用之前构建好的镜像作为基础层,然后一步一步添加新的层。

  • 容器:

    镜像image和容器container的关系,就像是类和实例的关系一样。容器的实质是进程,运行于属于自己
    独立的命名空间,所以是一个完全隔离的环境。

  • 仓库:

    我们需要一个集中存储发布镜像的服务,那就是镜像仓库,



1.2 安装运行Docker:

首先删除旧版本的docker:

sudo apt-get remove docker docker-engine docker.io

因为网络原因建议使用国内源,首先为了确保下载的软件包的合法性,需要添加软件源的GPG秘钥

curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

然后我们需要在sources.list中添加Docker软件源:

sudo add-apt-repository \
    "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

最后安装docker-ce:

sudo apt-get install docker-ce docker-ce-cli containerd.io

启动docker:

sudo systemctl enable docker
sudo systemctl start docker

建立docker用户组: docker命令会使用unix socket与docker引擎通讯,而只有root用户和docker
组的用户才可以访问docker引擎的Unix socket。

sudo groupadd docker
sudo usermod -aG docker $USER

这一步之后需要退出当前终端进行重新登录,然后如下测试是否安装成功:

docker run hello-world


1.3 配置镜像加速:

直接从Docker Hub拉取镜像可能会遇到困难,所以可以配置多个国内镜像
查看是否在docker.service文件中配置过镜像的地址:从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。

systemctl cat docker | grep '\-\-registry\-mirror'

如果没有任何输出那么就可以在/etc/docker/daemon.json中写入,不存在这个文件可以重新创建

{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ]
}

重启服务:

sudo systemctl daemon-reload
sudo systemctl restart docker

2. Docker镜像

2.1 镜像仓库

docker login  # 交互式输入用户名和密码来登录docker hub
docker logout  # 退出登录
docker search centos  # 在镜像仓库里面查找
docker pull centos  # 下载镜像
docker push username/ubuntu:18.04  # 本地镜像上传

2.2 下载,列出,删除镜像

Docker hub上面有很多高质量的镜像,获取镜像的命令是docker pull:

docker pull [ip:port] 仓库名[:标签]

上面可以指定镜像仓库的地址,默认是docker.io也就是Docker hub
仓库名是一个两段式的名称,<用户名>/<软件名>,如果不给用户名那么默认就是library,也就是官方镜像

EX:

$ docker pull ubuntu:18.04

18.04: Pulling from library/ubuntu
bf5d46315322: Pull complete
9f13e0ac480c: Pull complete
e8988b5b3097: Pull complete
40af181810e7: Pull complete
e6f7c7e5c03e: Pull complete
Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
Status: Downloaded newer image for ubuntu:18.04

之前说过docker镜像有分层储存的概念,那么下载也是一层一层去下载,而不是下载一个单一文件

列出镜像

docker image ls
docker system df  # 查看镜像,容器,数据占用的空间
docker image ls -a  # 因为docker镜像是一层一层的,那么不同的镜像可能会使用同样的层,docker就会利用中间层镜像来加速镜像的构建,默认展示的是顶层的镜像,如果想要查看所有可以使用

删除镜像

docker image rm ...  # 可以指定镜像短ID,镜像长ID,镜像名或者镜像摘要来删除, 一般用ID前三个字符足够区分就可以了

docker image rm $(docker image ls -q redis)  # 需要删除所有仓库名为 redis 的镜像

运行镜像

docker run -it --rm ubuntu:18.04 bash

2.3 制作镜像

2.3.1 docker commit:

首先docker commit可以用来保存镜像,但是一般是学习使用,因为一切操作都相当于是黑箱操作,无法维护。制作定制镜像应该使用Dockerfile来实现。

2.3.2 Dockerfile:

sample:

FROM nginx  # 指定基础镜像,如果完全不需要,那就指定scratch为空白镜像
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html  # RUN执行命令行命令,每一个指令都会建立新的一层,但是目前的最大层数是127层,所以不能太臃肿,所以如果我们要安装redis,我们应该像下面这样:

RUN set -x; buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && ...
# 合并在一起的run指令都是做同一件事情,这里最好是安装好了后,把所以不需要的文件都删除干净。因为每一层的东西并不会在下一层被删除,所以在我们构建的时候,一定要做到每一层只需要添加真正需要的东西,其他任何无关的东西都应该清理。

2.3.3 构建镜像

写好了Dockerfile之后,我们可以在文件所在的目录里面执行:

docker build -t nginx:v3 .  # 注意这里有个“点”,其实是指定了构建镜像的上下文的

docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world  # 还可以直接从git repo构建,上面指定了git repo的地址,并且指定分支为master,构建的目录是`/amd64/hello-world`

**Docker build工作原理 !!! **

Docker在运行的时候分为Docker引擎(也就是服务端守护进程)和客户端工具,Docker引擎提供了一组REST API,那么docker命令这样的客户端工具,其实都是通过Docker引擎提供的REST API进行交互。

所以表面上我们看起来是本机执行各种docker功能,但是实际上都是在远程调用在服务端完成,这是一种C/S设计。

镜像构建时候的上下文

上面构建镜像的时候我们可以看到最后其实指定了一个目录., 表示当前目录,这里很容易觉得这个路径指的是Dockerfile的位置,但是实际上不是的,是docker build时候的上下文路径。

那么在我们构建镜像的时候我们经常会执行COPYADD等指令来将本地文件复制进镜像,那么构建镜像其实在服务端而不是本地构建,所以当构建的时候,用户会指定镜像上下文的路径,然后会将路径下的所有内容打包,然后上传到Docker引擎,这样构建的时候,服务端就会获取一切构建镜像所需的一切文件。所以COPY ./package.json /app/,这个命令实际上是在上下文路径里面找文件,当然如果你用COPY /opt/xxxx这种命令肯定是无法工作的。如果在上下文路径排除一些文件上传到docker引擎,那么我们可以像.gitignore一样的语法写`.dockerignore

2.3.4 构建镜像的常用命令:

# COPY 复制文件
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY package.json /usr/src/app/  # 从构建上下文目录中的文件路径,复制到新的一层镜像中的目标路径
# ADD 高级复制文件
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /  # ADD命令和copy一样,但是增加了功能,比如源路径可以是一个url,那么会先下载再复制,或者源文件是一个压缩文件,那么会自动解压这个文件到目标位置。
# CMD 容器启动命令
CMD <命令>  CMD ["可执行文件", "参数1", "参数2"...]
CMD echo $HOME  # docker不是虚拟机,是容器也就是进程,那么最后需要指定所运行的程序和参数
CMD [ "sh", "-c", "echo $HOME" ]  # 实际推荐使用exec格式
# 容器不是虚拟机,里面的应用都应该以前台的方式执行!!!!!,而不是用什么systemd启动后台服务
CMD ["nginx", "-g", "daemon off;"]  # 正确的应该是直接执行nginx可执行文件,并且要求前台方式运行
# ENTRYPOINT 入口点
# ENTRYPOINT 的目的和 CMD 一样,但是适用于一些特殊场景

# 场景一:让镜像变成像命令一样使用
FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://myip.ipip.net" ]
# 当我们想查询当前公网ip,直接运行镜像即可,但是如果我们希望这个还能加上参数,如果我们使用docker run myip -i,会报错,因为跟在镜像名后面的是 command,运行时会替换 CMD 的默认值。但是-i不是命令。所以必须完整的输入命令:
docker run myip curl -s http://myip.ipip.net -i  # 这个会完全替换CMD执行的东西,但是不好用
# 所以我们可以使用ENTRYPOINT使镜像名后面可以直接接上需要添加的参数:
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]

# 场景二:应用运行前的准备工作,比如数据库配置初始化,可以参考redis服务
# ENV 设置环境变量
ENV <key1>=<value1> <key2>=<value2>...
ENV VERSION=1.0 DEBUG=on  # 使用变量的时候就是用$VERSION
# ARG构建参数
ARG DOCKER_USERNAME=library  # 和ENV不同的是,这个只会在构建环境时候生效,在将来容器运行的时候不会存在。
# VOLUMN 定义匿名卷
VOLUME ["<路径1>", "<路径2>"...]
VOLUME /data  # 这里的 /data 目录就会在容器运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化
# EXPOSE 暴露端口
EXPOSE <端口1> [<端口2>...]  # 指明容器运行时提供服务的端口,但是这只是一个声明,没有实际的作用。这样写也是为了帮助镜像的使用者理解这个镜像服务的守护端口,以便方便配置映射。
# WORKDIR 指定工作目录
WORKDIR /app  # 改变后面各层的工作目录位置
# USER 指定当前用户
RUN groupadd -r redis && useradd -r -g redis redis
USER redis  # 和上面的WORKDIR类似,都是改变环境状态并且影响后面的层。这里是切换用户
RUN [ "redis-server" ]
# HEALTHCHECK 健康检查
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
	CMD curl -fs http://localhost/ || exit 1
# LABEL 为镜像添加元数据
LABEL <key>=<value> <key>=<value> <key>=<value> ...  # 申明镜像作者,文档地址等等

最后构建镜像:

docker build -t go/helloworld:3 .

3. Docker容器

3.1 启动容器:

当我们使用docker run来创建容器的时候,后台创建的标准流程是:

  1. 检查本地是否存在指定的镜像,不存在就去仓库里面拉
  2. 利用镜像创建并启动一个容器
  3. 分配一个文件系统,并且在只读的镜像层的外面再挂载一层可读写层
  4. 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器里面。
  5. 从地址池配置一个ip地址给容器
  6. 执行用户指定的应用程序
  7. 执行完毕后容器会被终止

有下面几种常用的容器启动方式:

# 1. 直接使用容器的环境执行一些命令
docker run ubuntu:18.04 /bin/echo 'Hello world'

# 2. 启动docker环境里面的bash终端,允许用户进行交互
docker run -t -i ubuntu:18.04 /bin/bash  # `-t`是让docker分配一个伪终端并绑定到容器的标准输入上,`-i`是让容器的标准输入保持打开的状态。

# 3. 将一个之前终止的容器再启动
docker container start

# 4. 守护态运行容器  -d
docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"  # 更多时候我们需要让docker在后台运行而不是直接把命令的输出结果输出到当前的宿主机下,他会返回一个容器的唯一id。
docker container logs [container-id]  # 可以这样查看容器的日志

3.2 终止容器:

docker container stop
docker container ls -a  # 可以看到终止状态的容器
docker container start
docker container restart

3.3 进入容器

# 进入容器可以使用docker attach和docker exec,推荐使用docker exec!!!

# 使用docker attach
docker run -dit ubuntu  # 输出:243c32... docker container id
docker attach 243c  # 进入容器,如果从这个stdin中exit,会导致容器的终止

# 使用docker exec
docker run -dit ubuntu
docker exec -it 69d1 bash  # -it参数一起使用是为了让我们看到熟悉的Linux命令提示符,并且这样如果exit不会导致容器的终止。

3.4 导入导出容器:

docker export 7691a814370e > ubuntu.tar  # 根据容器id导出容器快照到本地
docker import http://example.com/exampleimage.tgz example/imagerepo  # 指定url或者某个目录导入

3.5 删除容器:

docker container rm trusting_newton  # 删除一个处于终止状态的容器
docker container rm -f trusting_newton  # 添加-f参数,删除一个运行中的容器
docker container prune  # 清理掉所有处于终止状态的容器

4. Docker数据卷

数据卷是一个可供一个或者多个容器使用的特殊目录,它主要有几个特性:数据卷可以在容器之间共享,对数据卷的修改会立马生效,对数据卷的更新不会影响镜像,数据卷默认会一直存在(即使容器删除了)

创建一个数据卷:

docker volume create my-vol  # 创建一个数据卷:my-vol
docker volume ls  # 查看数据卷
 
$ docker volume inspect my-vol  # 查看指定数据卷的信息
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",  # 数据卷在本地的位置
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

创建容器并挂载数据卷:

$ docker run -d -P \
    --name web \
    # -v my-vol:/usr/share/nginx/html \
    --mount source=my-vol,target=/usr/share/nginx/html \
    nginx:alpine  # docker run的时候可以使用--mount来将数据卷挂载到容器上,可以挂载多个,这里将数据加载到容器的/usr/share/nginx/html目录
    
$ docker inspect web  # 再查看容器的信息,可以看到/var/lib/docker/volumes/my-vol/_data目录挂载到了容器的/usr/share/nginx/html目录
"Mounts": [
    {
        "Type": "volume",
        "Name": "my-vol",
        "Source": "/var/lib/docker/volumes/my-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

清理数据卷:

$ docker volume rm my-vol  # 删除数据卷
$ docker volume prune  # 清理无主的数据卷

直接挂载本地主机目录到容器内:

# 使用--mount标记通过指定source和target可以直接挂载一个本地主机目录到容器中:
$ docker run -d -P \
    --name web \
    # -v /src/webapp:/usr/share/nginx/html \
    --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
    nginx:alpine

5. Docker Compose

5.1 基本用法

使用一个dockerfile文件可以方便定义一个应用容器,但是我们经常会需要多个容器相互配合来完成某项任务,比如我们实现一个web项目,那么需要web服务容器+数据库容器+nginx容器等等。那么compose满足了这样的需求,它通过docker-compose.yml文件来定义一组相互关联的容器为一个项目

通过sudo pip install -U docker-compose安装

打比方我们有一个场景包含了web应用和redis缓存:

针对于web应用我们有一个Dockerfile

FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]  # 这里app.py是一个flask web应用的入口

因为redis已经有制作好的docker镜像,所以我们直接使用这个web应用的镜像和redis镜像编写

docker-compose.yml

version: '3'
services:

  web:  # 1. web应用
    build: .
    ports:
     - "5000:5000"

  redis:
    image: "redis:alpine"  # 2. redis应用

最后运行compose项目:$ docker-compose up

5.2 Django实战实例:

https://yeasy.gitbook.io/docker_practice/compose/django

FROM /xxx/centos7-base-v2:stable

LABEL Author="<xxxxxxx@gmail.com>"
LABEL Description="Image for a web service."

ARG GDAL_VERSION=2.2.4
COPY files /home/jaden/sample-web-project  # 把镜像上下文目录里面的files文件夹拷贝到容器指定目录

RUN rm -f /var/lib/rpm/__db* \  # 打比方web程序需要安装GDAL
    && rpm --rebuilddb \
    && yum install python36 python3-devel -y \
    && yum install gcc gcc-c++ make gcc-devel -y \
    && yum install nginx -y \
    && chown -R jaden:jaden /home/jaden/sample-web-project \
    && tar xzf /home/jaden/sample-web-project/gdal-${GDAL_VERSION}.tar.gz \
    && cd gdal-${GDAL_VERSION} \
    && ./configure --prefix=/usr --libdir=/usr/lib64 \
    && make && sudo make install && sudo make clean \
    && rm -rf ../gdal-${GDAL_VERSION}.tar.gz

RUN yum install geos geos-devel -y \
    && yum install opencv opencv-devel libSM -y \
    && yum clean all \
    && python3 -m pip install --upgrade --force-reinstall pip -i http://xxx/pypi/stable/+simple/ --trusted-host xxx \
    && python3 -m pip install pipenv -i http://xxx/pypi/stable/+simple/ --trusted-host xxx \
    && rm -f /home/jaden/nginx/conf/conf.d/ngx_metric.conf  \
    && mv /home/jaden/sample-web-project/nginx.conf /home/jaden/nginx/conf/


EXPOSE 8000
EXPOSE 8080

CMD [ "/home/jaden/nginx/sbin/nginx", "-g", "daemon off;" ]
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值