下一篇 002 - k8s -安装k8s集群

Docker

安装

卸载旧版本

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

安装一些依赖包

sudo yum install -y yum-utils

添加 stable 版本的 docker 仓库

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

建议使用阿里云的镜像进行加速,要不太慢了

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

列出docker的可安装版本

yum list docker-ce --showduplicates | sort -r

安装docker

sudo yum install docker-ce-20.10.9 docker-ce-cli-20.10.9 containerd.io -y

设置为开机启动

sudo systemctl enable docker && sudo systemctl daemon-reload

仓库地址

cd /etc && mkdir docker && cd docker && vi daemon.json
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

启动 docker

systemctl start docker

启动后查看docker相关信息

sudo docker info

*** 运行容器时出现报错

docker: Error response from daemon: cannot start a stopped process: unknown.

可以执行下面命令后, 再重新运行容器

sudo yum install -y libseccomp-devel

基本操作

列出指定镜像的可用版本

-------dockertags.sh---------------

#!/bin/bash
function usage() {
cat << HELP
dockertags  --  list all tags for a Docker image on a remote registry.
EXAMPLE: 
    - list all tags for ubuntu:
       dockertags ubuntu
    - list all php tags containing apache:
       dockertags php apache
HELP
}
 
 
 
if [ $# -lt 1 ]; then
    usage
    exit
fi
 
image="$1"
tags=`wget -q https://registry.hub.docker.com/v1/repositories/${image}/tags -O -  | sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | awk -F: '{print $3}'`
 
if [ -n "$2" ]; then
    tags=` echo "${tags}" | grep "$2" `
fi
echo "${tags}"

然后执行命令

./dockertags.sh ubuntu

镜像操作

拉取

docker pull ubuntu:18.04
docker images

删除

docker rmi -f ubuntu:18.04

给镜像重新打上一个 tag

docker tag ubuntu:18.04 zcw/ubuntu:18.05
docker images

将镜像导出成一个独立的文件

docker save ubuntu >./ubuntu.tar.gz

从文件导入

docker rmi -f ubuntu:18.04
docker images
docker load <./ubuntu.tar.gz
docker images

运行容器

进程结束时立即删除容器

docker run -it --rm ubuntu:18.04 /bin/bash

/bin/bash命令会在容器中常驻前台,所以容器会一直运行
如果执行一个不会常驻前台的命令 例如ls, 执行后容器会立刻结束运行
所以运行nginx不要用daemon模式, 因为nginx执行后就退出到后台, 容器会立刻结束运行

前台运行容器循环打印

docker run --rm ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

后台运行容器并用日志查看打印

docker run -d --rm ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
docker logs -f 35c4727a2e66
docker stop 35c4727a2e66

因为用–rm, 退出容器随即将会被删除

网络

查看容器中的网络

启动一个容器

docker run -tid --net=bridge --name docker_bri busybox top

登入容器

docker exec -it docker_bri /bin/sh

查看容器内的ip地址

ifconfig -a

在容器内查看网关

route -n

在启动一个容器与docker_bri容器连接

docker run -tid --link docker_bri --name docker_bri1 busybox top

登入容器

docker exec -it docker_bri1 /bin/sh

尝试连接

ping docker_bri

但–link是单方向的, 所以反过来就不行

创建自定义网桥网络实现互联互通

创建一个自定义网络

docker network create -d bridge my-net

查看自定义网络

docker network ls

启动容器并连入my-net网络

docker run -it --rm --name busybox1 --network my-net busybox sh

打开另外一个命令行窗口

docker run -it --rm --name busybox2 --network my-net busybox sh

然后在两个容器中分别取ping另一个容器, 发现两个容器是可以连通的

ping busybox1
ping busybox2

数据卷

数据卷是可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 数据卷默认会一直存在,即使容器被删除

创建数据卷

docker volume create my-vol
docker volume ls
docker volume inspect my-vol

[
{

“Mountpoint”: “/var/lib/docker/volumes/my-vol/_data”,
…这个目录是宿主机中的数据卷目录
}
]

容器挂载数据卷

启动一个挂载数据卷的容器

docker run -d -p 8080:80 --name web -v my-vol:/usr/share/nginx/html nginx
curl http://localhost:8080

修改数据卷内容之后重新访问

echo "Hello Docker" > /var/lib/docker/volumes/my-vol/_data/index.html
curl http://localhost:8080

想要删除容器时自动删除数据卷可以用docker rm -v(这个暂时没研究明白)

删除没有关联容器的数据卷并查看效果

docker volume prune
docker volume ls
cat /var/lib/docker/volumes/my-vol/_data/index.html

挂载主机目录

docker run -it -v /tmp:/usr/tmp busybox /bin/sh

默认挂载的路径权限为读写。如果指定为只读可以用:ro,如:-v /tmp:/usr/tmp:ro
– 容器目录不可以为相对路径
– 宿主机目录如果不存在,则会自动生成
– 挂载宿主机已存在目录后,在容器内对其进行操作,报“Permission denied”。可通过两种方式解决:

  • 1> 关闭selinux。
    临时关闭:# setenforce 0
    永久关闭:修改/etc/sysconfig/selinux文件,将 SELINUX 的值设置为disabled。
  • 2> 以特权方式启动容器
    指定--privileged参数,如:
    # docker run -it --privileged=true -v /test:/soft centos /bin/bash

数据卷和挂载目录的不同

  1. volume 会引起 docker 目录膨胀,因为既要存镜像,又要存 volume,最好不要放在系统盘,将 docker 的安装目录配置到其他更大的挂载盘

  2. 两者有一个不同的行为:当容器外的对应目录是空的,volume 会先将容器内的内容拷贝到容器外目录,而 mount 会将外部的目录覆盖容器内部目录

  3. volume 还有一个不如 bind mount 的地方,不能直接挂载文件,例如挂载 nginx 容器的配置文件:nginx.conf

Dockerfile

FROM

指定基础镜像, Dockerfile的必须是第一条指令
Docker Hub上有很多官方镜像
服务类: nginx, redis, mongo, mysql, httpd, php, tomcat
语言环境: node, openjdk, python, ruby, golang
操作系统: ubuntu, debian, centos, alpine
此外, scratch是一个虚拟的镜像, 并不实际存在, 表示一个空白的镜像.
使用 Go 语言 开发的应用很多会使用这种方式来制作镜像.

FROM scratch

RUN

shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式

RUN echo 'Hello, Docker!' > /usr/share/nginx/html/index.html

exec 格式:RUN [“可执行文件”, “参数1”, “参数2”],这更像是函数调用中的格式。既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

WORKDIR

WORKDIR 指令设置 Dockerfile 中的任何 RUN, CMD, ENTRPOINT, COPY 和 ADD 指令的工作目录. 如果 WORKDIR 指定的目录不存在, 即使随后的指令没有用到这个目录, 都会创建该目录
WORKDIR 可以用来替代类似于 RUN cd … && do-something 的指令

ADD & COPY

COPY 指令和 ADD 指令都可以将主机上的资源复制或加入到容器镜像中
ADD 还能够将远程 URL 所对应的文件或目录, 作为资源复制到镜像文件系统

格式用法

exec 格式用法:COPY ["",… “”], 特别适合路径中带有空格的情况.
shell 格式用法:COPY …

COPY基本用法

$ echo 'Hello Docker!' > index.html

然后修改 Dockerfile

FROM nginx
COPY index.html /user/share/nginx/html/index.html

ADD & COPY 注意事项

  • 源路径可以有多个
  • 源路径是相对于执行 build 的相对路径
  • 源路径如果是本地路径,必须是构建上下文中的路径
  • 源路径如果是一个目录,则该目录下的所有内容都将被加入到容器,但是该目录本身不会
  • 目标路径必须是绝对路径,或相对于 WORKDIR 的相对路径
  • 目标路径如果不存在,则会创建相应的完整路径
  • 目标路径如果不是一个文件,则必须使用/结束
  • 路径中可以使用通配符

ADD

ADD远程拉取
ADD http://foo.com/bar.go /tmp/main.go
由于 ADD 指令不支持认证,如果从远程获取资源需要认证,则只能使用RUN wget 或 RUN curl 替代了

ADD自动解压文件
ADD /foo.tar.gz /tmp/
上述指令会使 foo.tar.gz 压缩文件解压到容器的 /tmp 目录

构建镜像

一般格式

docker build [选项] <上下文路径/URL/->

操作示例

Dockerfile

FROM nginx
RUN echo 'Hello, Dockerfile!' > /usr/share/nginx/html/index.html

执行构建

docker build -t nginx:v1 .

-t 参数指定了最终镜像的名称 nginx:v1
. 表示当前目录, 而 Dockerfile 就在当前目录

docker run -dit --rm -p 8080:80 --name nginx1 nginx:v1
curl http://localhost:8080

构建上下文

  • 当构建的时候, 用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后, 会将路径下的所有内容打包, 然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后, 展开就会获得构建镜像所需的一切文件
  • 因此, COPY 这类指令中的源文件的路径都是相对路径. 如果真的需要那些文件, 应该将它们复制到上下文目录中去.
  • 刚才的命令docker build -t nginx:v1 .中的这个. 实际上是在指定上下文的目录, docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像.

如果观察 docker build 输出,我们其实已经看到了这个发送上下文的过程:
$ docker build -t nginx:v1 .
Sending build context to Docker daemon 2.048 kB

一般来说, 应该会将 Dockerfile 置于一个空目录下, 或者项目根目录下.如果该目录下没有所需文件, 那么应该把所需文件复制一份过来.如果目录下有些东西确实不希望构建时传给 Docker 引擎, 那么可以用 .gitignore 一样的语法写一个 .dockerignore, 该文件是用于剔除不需要作为上下文传递给 Docker 引擎的.

那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢?这是因为在默认情况下, 如果不额外指定 Dockerfile 的话, 会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile.这只是默认行为, 实际上 Dockerfile 的文件名并不要求必须为 Dockerfile, 而且并不要求必须位于上下文目录中, 比如可以用-f ../Dockerfile.Dev参数指定某个文件作为 Dockerfile.

私有仓库

启动仓库容器

docker run -d \
    -p 5000:5000 \
    -v /opt/data/registry:/var/lib/registry \
    registry:2

拉取共有仓库镜像

docker pull ubuntu
docker images

给镜像打tag

docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest

上传到私有仓库

docker push 127.0.0.1:5000/ubuntu:latest

查看私有仓库是否上传成功

curl 127.0.0.1:5000/v2/_catalog

删除本地镜像

docker image rm 127.0.0.1:5000/ubuntu:latest
docker images

拉取私有仓库镜像

docker pull 127.0.0.1:5000/ubuntu:latest
docker images

Dockerfile示例1

FROM golang:1.11-alpine AS build

# 安装项目需要的工具
# 运行 `docker build --no-cache .` 来更新依赖
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# 通过 Gopkg.toml 和 Gopkg.lock 获取项目的依赖
# 仅在更新 Gopkg 文件时才重新构建这些层
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# 安装依赖库
RUN dep ensure -vendor-only

# 拷贝整个项目进行构建
# 当项目下面有文件变化的时候该层才会重新构建
COPY . /go/src/project/
RUN go build -o /bin/project

# 将打包后的二进制文件拷贝到 scratch 镜像下面,将镜像大小降到最低
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]

构建缓存

在镜像的构建过程中,Docker 根据 Dockerfile 指定的顺序执行每个指令。在执行每条指令之前,Docker 都会在缓存中查找是否已经存在可重用的镜像,如果有就使用现存的镜像,不再重复创建。当然如果你不想在构建过程中使用缓存,你可以在 docker build 命令中使用 --no-cache=true 选项。Docker 中构建缓存遵循的基本规则如下:

  • 从一个基础镜像开始(FROM 指令指定),下一条指令将和该基础镜像的所有子镜像进行匹配,检查这些子镜像被创建时使用的指令是否和被检查的指令完全一样。如果不是,则缓存失效。
  • 对于 ADD 和 COPY 指令,镜像中对应文件的内容也会被检查,每个文件都会计算出一个校验值。在缓存的查找过程中,会将这些校验和已存在镜像中的文件校验值进行对比。如果文件有任何改变,则缓存失效。
  • 除了 ADD 和 COPY 指令,缓存匹配过程不会查看临时容器中的文件来决定缓存是否匹配。例如,当执行完 RUN apt-get -y update 指令后,容器中一些文件被更新,但 Docker 不会检查这些文件。这种情况下,只有指令字符串本身被用来匹配缓存。
  • 一旦缓存失效,所有后续的 Dockerfile 指令都将产生新的镜像,缓存不会被使用。

使用多阶段构建

多阶段构建可以让我们大幅度减小最终的镜像大小,而不需要去想办法减少中间层和文件的数量。因为镜像是在生成过程的最后阶段生成的,所以可以利用生成缓存来最小化镜像层。

例如,如果你的构建包含多个层,则可以将它们从变化频率较低(以确保生成缓存可重用)到变化频率较高的顺序排序:

  • 安装构建应用程序所需的依赖工具
  • 安装或更新依赖项
  • 构建你的应用

避免安装不必要的包

为了降低复杂性、减少依赖、减小文件大小和构建时间,应该避免安装额外的或者不必要的软件包。例如,不要在数据库镜像中包含一个文本编辑器。

应用解耦

每个容器应用只关心一个方面的事情。将多个应用解耦到不同容器中,可以更轻松地保证容器的横向扩展和复用。例如一个 web 应用程序可能包含三个独立的容器:web应用、数据库、缓存,每个容器都是独立的镜像,分开运行。但这并不是说一个容器就只能跑一个进程,因为有的程序可能会自行产生其他进程,比如 Celery 就可以有很多个工作进程。虽然每个容器跑一个进程是一条很好的法则,但这并不是一条硬性的规定。我们主要是希望一个容器只关注一件事情,尽量保持干净和模块化。

如果容器互相依赖,你可以使用 Docker 容器网络 来把这些容器连接起来,我们前面已经跟大家讲解过 Docker 的容器网络模式。

最小化镜像层数

在很早之前的版本中尽量减少镜像层数是非常重要的,不过现在的版本已经有了一定的改善了:

  • 只有 RUN、COPY 和 ADD 指令会创建层,其他指令会创建临时的中间镜像,但是不会直接增加构建的镜像大小了。
  • 多阶段构建的支持,允许我们把需要的数据直接复制到最终的镜像中,这就允许我们在中间阶段包含一些工具或者调试信息了,而且不会增加最终的镜像大小。

对多行参数排序

只要有可能,就将多行参数按字母顺序排序。这可以帮助你避免重复包含同一个包,更新包列表时也更容易,也更容易阅读和审查。建议在反斜杠符号 \ 之前添加一个空格,可以增加可读性。比如下面的例子:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

官方仓库示例

https://github.com/docker-library/docs

东天目之大仙顶日出
枕戈待旦的爬上山顶整理装备准备出发

东天目之大仙顶日出. 经历两小时爬到大仙顶,看下时间六点刚过, 刚好赶上日出, 此处海拔约1450米, 脚下的土石混着腐植被冻在一层薄脆的冰壳上, 踩上去咔咔的响, 当地山民管这个叫"萝卜丝". 接下来准备出发,继续完成这一天35公里 累计爬升3780米的单日行程.

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐