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时候的上下文路径。
那么在我们构建镜像的时候我们经常会执行COPY
和ADD
等指令来将本地文件复制进镜像,那么构建镜像其实在服务端而不是本地构建,所以当构建的时候,用户会指定镜像上下文的路径,然后会将路径下的所有内容打包,然后上传到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
来创建容器的时候,后台创建的标准流程是:
- 检查本地是否存在指定的镜像,不存在就去仓库里面拉
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并且在只读的镜像层的外面再挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器里面。
- 从地址池配置一个ip地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器会被终止
有下面几种常用的容器启动方式:
# 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;" ]
标签:容器,全套,nginx,详解,&&,镜像,docker,Docker 来源: https://blog.csdn.net/qq_43355223/article/details/120866285