001 - k8s - docker基础和常用操作
安装 - 基本操作 - 网络 - 数据卷 - Dockerfile
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
数据卷和挂载目录的不同
volume 会引起 docker 目录膨胀,因为既要存镜像,又要存 volume,最好不要放在系统盘,将 docker 的安装目录配置到其他更大的挂载盘
两者有一个不同的行为:当容器外的对应目录是空的,volume 会先将容器内的内容拷贝到容器外目录,而 mount 会将外部的目录覆盖容器内部目录
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米的单日行程.
更多推荐
所有评论(0)