Docker 基础篇
Docker是一种运行在Linux和Windows上的软件,用于创建、管理和编排容器
安装
Ubuntu
移除旧版本
1
2
3$ sudo apt-get remove docker \
docker-engine \
docker.ioApt 安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28# 安装https传输的软件包和ca证书
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
# 添加软件源的 GPG 密钥
$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# 官方源
# $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 向 source.list 中添加 Docker 软件源
$ sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"
# 官方源
# $ sudo add-apt-repository \
# "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
# $(lsb_release -cs) \
# stable"Docker CE 安装、启动
1
2
3
4
5
6
7
8
9
10
11
12# 安装
$ sudo apt-get update
$ sudo apt-get install docker-ce
# 也可以通过脚本自动化安装
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
# 启动
$ sudo systemctl enable docker
$ sudo systemctl start docker建立docker用户组
1
2
3
4# 建立用户组
$ sudo groupadd docker
# 将当前用户添加到docker组
$ sudo usermod -aG docker $USER
MacOS
镜像
搜索
1
$ docker search ubuntu
获取镜像
1
2
3# 格式 docker (image) pull <Registry>/<userName of Orgnization>/<Repository>:<Tag>
# eg
$ docker pull ubuntu:latest列举镜像
1
2
3
4
5
6
7
8# 列举格式 $ docker images <repository>:<tag>
# filter 参数过滤, 列举所有tag 为latest的镜像 (dangling, before, since, label, reference)
$ docker image ls --filter reference="*:latest"
# 格式化显示, 注意格式(大小写、标点)
$ docker images --format "{{.Repository}}: {{.Size}}"
$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
# 删除悬虚镜像
$ docker image prune删除本地镜像
1
2
3
4
5# 删除格式, 其中镜像是镜像ID(长或短)、镜像名、镜像摘要
$ docker image rm [选项] <镜像1> [<镜像2> ...]
# 删除所有名为redis的镜像
$ docker rmi $(docker image ls -q redis)更新镜像
当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个
docker commit
命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。1
2
3# 产看容器内的变化
# 格式 docker diff <容器ID或容器名>
$ docker diff webserver1
2
3
4
5
6
7
8
9
10
11
12
13
14# 格式 $ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
# 例如, 在nginx容器中修改内容后
$ docker commit \
--author "jingyucute@gmail.com" \
--message "update content" \
webserver \
nginx:v2
# 或者简写
$ docker commit \
-a="jingyucute@gmail.com" \
-m="update content" \
webserver \
nginx:v2
1
2
3# 产看镜像内的提交记录
# 格式 docker history <仓库名>[:<标签>
$ docker history nginx:v2特别注意
docker commit
命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用docker commit
定制镜像,定制镜像应该使用Dockerfile
来完成。使用docker commit
意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。这种黑箱镜像的维护工作是非常痛苦的。使用Dockerfile制作镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 格式 docker build [选项] <上下文路径/URL/->
$ docker build -t <repository>:<tag> .
# 直接从Git repo中构建
$ docker build https://github.com/twang2218/gitlab-ce-zh.git
# 用给定的 tar 压缩包构建, 下载后会自动解压
$ docker build http://server/context.tar.gz
# 标准输入构建
$ docker build - < context.tar.gz
$ docker build - < Dockerfile
$ cat Dockerfile | docker build -
容器
查看
1
2
3# 查看正在运行的容器, 加上-a参数表示查看所有的容器(包括退出的)
$ docker container ls [-a]
$ docker ps [-a]启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 从镜像中创建并启动一个容器 , 参数自己查吧
# 格式 docker <container> run [--options]
# 交互式运行ubuntu容器
# -i: 交互式操作。
# -t: 终端。
# ubuntu: ubuntu 镜像。
# /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash
$ docker run -it ubuntu --name myOS /bin/bash
# 守护态运行一个容器
# 格式 docker <container> run -d
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
# 启动一个停止了的容器
# 格式 docker <container> start <Container-name-or-ID>
$ docker start myOS重启
1
2# 格式 docker <container> restart <Container-name-or-ID>
$ docker restart myOS查看容器输出
1
2# 格式 docker <container> logs <Container-name-or-ID>
$ docker logs myOS停止
1
2# 格式 docker <container> stop <Container-name-or-ID>
$ docker stop myOS删除
1
2
3
4
5
6# 删除的容器不能是运行状态,可以先停止再删除, 也可以通过加上 -f 参数强删(不推荐)
# 格式 docker container rm <Container-name-or-ID> [-f]
$ docker rm <Container-name-or-ID>
# 清楚所有停止状态的容器
$ docker container prune进入容器
1
2
3
4
5
6
7
8
9
10
11
12# 两种方式, 推荐使用方式二
# 方式一 docker attach <Container-name-or-ID>
# 这种方式退出终端,会导致容器停止
$ docker attach myOS
# 方式一 docker attach <Container-name-or-ID>
# 这种方式退出终端,会导致容器停止
$ docker attach myOS
# 方式二 docker exec -it <Container-name-or-ID> bash
# 这种方式退出终端,容器不会停止
$ docker exec -it myOS bash
Dockerfile参数说明
FROM
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。
FROM
就是指定 基础镜像,因此一个Dockerfile
中FROM
是必备的指令,并且必须是第一条指令。1
2FROM nginx
...RUN
RUN
指令是用来执行命令行命令的。通常有两种形式: shell格式
RUN <命令>
(常用格式)1
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exec格式
RUN ["可执行文件", "参数1", "参数2"]
. 这种形式好比函数调用1
2
3RUN ["./test.php", "dev", "offline"]
# ====
RUN ./test.php dev offline注意
这里的RUN要尽量少, 多了会导致镜像臃肿, 因为镜像是按照层来构建的,一个
RUN
就是一层。所以可以使用&&
来链接命令,使用\
来换行举个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25FROM debian:stretch
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
# --------------------------------------------------------->
FROM debian:stretch
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-5.0.3.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 $buildDepsCOPY
COPY
指令将从构建上下文目录中<源路径>
的文件/目录复制到新的一层的镜像内的<目标路径>
位置1
2
3
4
5
6# 格式 COPY [--chown=<user>:<group>] <源路径>... <目标路径>
# COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
COPY hom?.txt /mydir/
# 改变文件的所属用户和用户组 --chown=<user>:<group>
COPY --chown=55:mygroup files* /mydir/ADD
ADD
指令和COPY
的格式和性质基本一致(同样需求下, 推荐使用COPY
)。- ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
- ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
在
COPY
和ADD
指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY
指令,仅在需要自动解压缩的场合使用ADD
1
2# 改变文件的所属用户和用户组 --chown=<user>:<group>
ADD --chown=55:mygroup files* /mydir/CMD
CMD
指令的格式和RUN
相似。- CMD 在docker run 时运行。
- RUN 是在 docker build。
CMD
也有两种形式1
2
3
4
5
6
7# shell 格式:CMD <命令>
# exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
CMD echo $HOME
# ====
CMD [ "sh", "-c", "echo $HOME" ]Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用
systemd
去启动后台服务,容器内没有后台服务的概念。1
2
3# CMD service nginx start
# 这个命令是将nginx作为后台服务来运行的, 应该写为一下形式
CMD ["nginx", "-g", "daemon off;"]ENTRYPOINT
ENTRYPOINT
的格式和RUN
指令格式一样,分为exec
格式和shell
格式。ENTRYPOINT
的目的和CMD
一样,都是在指定容器启动程序及参数。ENTRYPOINT
在运行时也可以替代,不过比CMD
要略显繁琐,需要通过docker run
的参数--entrypoint
来指定。当指定了
ENTRYPOINT
后,CMD
的含义就发生了改变,不再是直接的运行其命令,而是将CMD
的内容作为参数传给ENTRYPOINT
指令。就可以理解为<ENTRYPOINT> "<CMD>"
1
# 格式 ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
ENV
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
1
2
3
4
5# 格式 ENV <key> <value>
# ENV <key1>=<value1> <key2>=<value2>...
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"ARG
ARG
构建参数和ENV
的效果一样,都是设置环境变量。所不同的是,ARG
所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。1
# 格式 ARG <参数名>[=<默认值>]
该默认值可以在构建命令
docker build
中用--build-arg <参数名>=<值>
来覆盖。VOLUME
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中
1
2
3# 格式 VOLUME ["<路径1>", "<路径2>"...]
# VOLUME <路径>
VOLUME /data1
docker run -d -v mydata:/data
在这行命令中,就使用了
mydata
这个命名卷挂载到了/data
这个位置,替代了Dockerfile
中定义的匿名卷的挂载配置。EXPOSE
EXPOSE
指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
1
# 格式 EXPOSE <端口1> [<端口2>...]
要将
EXPOSE
和在运行时使用-p <宿主端口>:<容器端口>
区分开来。-p
,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而EXPOSE
仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。WORKDIR
使用
WORKDIR
指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录。1
# 格式 WORKDIR <工作目录路径>
谈一下误区
初学者将
Dockerfile
等同于 Shell 脚本来书写,会出现一下错误1
2RUN cd /app
RUN echo "hello" > world.txt将这个
Dockerfile
进行构建镜像运行后,会发现找不到/app/world.txt
文件,或者其内容不是hello
。原因: 在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;在
Dockerfile
中,这两行RUN
命令的执行环境根本不同,是两个完全不同的容器每一个
RUN
都是启动一个容器、执行命令、然后提交存储层文件变更。第一层RUN cd /app
的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。USRE
USER
指令和WORKDIR
相似,都是改变环境状态并影响以后的层。WORKDIR
是改变工作目录,USER
则是改变之后层的执行RUN
,CMD
以及ENTRYPOINT
这类命令的身份(用户和用户组必须提前已经存在)。1
# 格式 USER <用户名>[:<用户组>]
- 标题: Docker 基础篇
- 作者: Yaurora
- 创建于 : 2020-07-31 16:40:08
- 更新于 : 2020-08-01 17:22:24
- 链接: https://jingyu.life/2020/07/31/docker/basic/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。