805 lines
24 KiB
Markdown
805 lines
24 KiB
Markdown
# 02 Docker容器管理及镜像制作
|
||
|
||
docker-hub账号276267003 密码33
|
||
|
||
daocloud账号18612540627 密码33
|
||
|
||
## 一、Docker容器管理
|
||
|
||
**创建新容器但不启动**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker create -it daocloud.io/library/centos:5 /bin/bash
|
||
```
|
||
|
||
**创建并运行一个新容器**
|
||
同一个镜像可以启动多个容器,每次执行run子命令都会运行一个全新的容器
|
||
|
||
```
|
||
[root@qfedu.com ~]# docker run -itd --name="test" --restart=always centos /bin/bash
|
||
```
|
||
|
||
-i 捕获标准输入输出
|
||
-t 分配一个终端或控制台
|
||
-d 后台运行
|
||
--restart=always 容器随docker engine自启动,因为重启docker时默认容器都会被关闭,也适用于create子命令
|
||
--rm 当你仅仅需要短暂的运行一个容器且数据不需保存,你可能就希望Docker能在容器结束时自动清理其所产生的数据。这时就需要--rm参数了。注意:--rm 和 -d不能共用
|
||
|
||
--name="容器名称" 为容器分配一个名字,如果没有指定,docker会自动分配一个随机名称
|
||
可以通过三种方式调用容器:
|
||
1)使用UUID长命名("f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778")
|
||
2)使用UUID短Id("f78375b1c487")
|
||
3)使用Name("evil_ptolemy")
|
||
|
||
UUID标识是由Docker deamon生成的。
|
||
如果执行docker run时没指定--name,那么deamon会自动生成一个随机字符串。
|
||
但是对于一个容器来说有个name会非常方便,当需要连接其它容器时或者需要区分其它容器时,使用容器名可以简化操作。无论容器运行在前台或者后台,这个名字都是有效的。
|
||
|
||
**保存容器PID**
|
||
如果在使用Docker时有自动化的需求,你可以将containerID输出到指定的文件中(PIDfile),类似于某些应用程序将自身ID输出到文件中,方便后续脚本操作。
|
||
--cidfile="文件名"
|
||
|
||
**断开与容器的连接且关闭容器**
|
||
|
||
```bash
|
||
root@d33c4e8c51f8 /#exit
|
||
快捷键ctrl+d
|
||
如果只想断开和容器的连接而不关闭容器可使用快捷键ctrl+p+q
|
||
```
|
||
|
||
**ps 查看容器**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]#docker ps //只查看运行状态的容器
|
||
```
|
||
|
||
**-a 查看所有容器**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]#docker ps -a
|
||
```
|
||
|
||
**-q 只查看容器id**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker ps -a -q
|
||
```
|
||
|
||
**-l 列出最近一次启动的容器(了解)**
|
||
|
||
```
|
||
[root@qfedu.com ~]# docker ps -l
|
||
```
|
||
|
||
**inspect 查看容器详细信息**
|
||
|
||
用于查看容器的配置信息,包含容器名、环境变量、运行命令、主机配置、网络配置和数据卷配置等。
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker inspect d95 //d95是我机器上运行的一个容器ID的前3个字符
|
||
```
|
||
|
||
比如:容器里在安装ip或ifconfig命令之前,查看网卡IP显示容器IP地址和端口号,如果输出是空的说明没有配置IP地址(不同的Docker容器可以通过此IP地址互相访问)
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器id
|
||
```
|
||
|
||
关于inspect的用法有很多,如果想多了解可向大强哥索取文档**"扩展-Docker 查看容器信息-格式化.md"**
|
||
|
||
**start 启动容器**
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker start name
|
||
```
|
||
|
||
**stop kill 关闭容器**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker stop name //正常关闭
|
||
[root@qfedu.com ~]# docker kill name //强制关闭容器
|
||
|
||
关闭所有running状态的容器
|
||
[root@qfedu.com ~]# docker kill $(docker ps -q)
|
||
```
|
||
|
||
**rm 删除容器**
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker rm 容器id或名称
|
||
要删除一个运行中的容器,添加 -f 参数
|
||
|
||
根据状态删除所有容器
|
||
[root@qfedu.com ~]# docker rm $(docker ps -qf status=exited)
|
||
```
|
||
|
||
**restart 重启容器**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker restart name
|
||
```
|
||
|
||
**pause 暂停容器**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker pause name
|
||
```
|
||
|
||
**unpause 恢复容器,与pause相对应**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker unpause name
|
||
```
|
||
|
||
**rename 修改容器名称**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker rename mytest test
|
||
```
|
||
|
||
**stats 动态显示容器的资源消耗情况,包括:CPU、内存、网络I/O**
|
||
|
||
**port 输出容器端口与宿主机端口的映射情况**
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker port blog
|
||
80/tcp -> 0.0.0.0:80
|
||
宿主机的80端口映射到容器blog的内部端口80,这样可通过宿主机的80端口查看容器blog提供的服务
|
||
```
|
||
|
||
**连接容器**
|
||
方法1、attach
|
||
|
||
```
|
||
[root@qfedu.com ~]# docker attach 容器id
|
||
前提是容器创建时必须指定了交互shell
|
||
```
|
||
|
||
方法2、exec
|
||
通过exec命令可以创建两种任务:后台型任务和交互型任务
|
||
交互型任务
|
||
|
||
```
|
||
[root@qfedu.com ~]# docker exec -it 容器id /bin/bash
|
||
root@68656158eb8e:/ ls
|
||
```
|
||
|
||
后台型任务
|
||
|
||
```
|
||
[root@qfedu.com ~]# docker exec 容器id touch /testfile
|
||
```
|
||
|
||
**监控容器运行**
|
||
可以使用logs、top、events、wait这些子命令
|
||
logs
|
||
可以通过使用docker logs命令来查看容器的运行日志,其中--tail选项可以指定查看最后几条日志,而-t选项则可以对日志条目附加时间戳。使用-f选项可以跟踪日志的输出,直到手动停止。
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker logs App_Container //不同终端操作
|
||
[root@qfedu.com ~]# docker logs -f App_Container
|
||
[root@qfedu.com ~]# docker run -itd --name mytest docker.io/centos /bin/sh -c "while true; do echo hello world; sleep 2; done"
|
||
37738fe3d6f9ef26152cb25018df9528a89e7a07355493020e72f147a291cd17
|
||
[root@qfedu.com ~]# docker logs mytest
|
||
hello world
|
||
hello world
|
||
|
||
docker run -itd --name mytest 298ec /bin/sh -c "while true; do echo hello world; sleep 2; done"
|
||
```
|
||
|
||
top
|
||
显示一个运行的容器里面的进程信息
|
||
[root@qfedu.com ~]# docker top birdben/ubuntu:v1
|
||
|
||
events
|
||
Get real time events from the server
|
||
实时输出Docker服务器端的事件,包括容器的创建,启动,关闭等。
|
||
[root@qfedu.com ~]# docker start loving_meninsky
|
||
loving_meninsky
|
||
[root@qfedu.com ~]# docker events //不同终端操作
|
||
2017-07-08T16:39:23.177664994+08:00 network connect
|
||
df15746d60ffaad2d15db0854a696d6e49fdfcedc7cfd8504a8aac51a43de6d4
|
||
(container=50a0449d7729f94046baf0fe5a1ce2119742261bb3ce8c3c98f35c80458e3e7a,
|
||
name=bridge, type=bridge)
|
||
2017-07-08T16:39:23.356162529+08:00 container start
|
||
50a0449d7729f94046baf0fe5a1ce2119742261bb3ce8c3c98f35c80458e3e7a (image=ubuntu,
|
||
name=loving_meninsky)
|
||
|
||
wait(X)
|
||
Block until a container stops, then print its exit code
|
||
--捕捉容器停止时的退出码
|
||
执行此命令后,该命令会"hang"在当前终端,直到容器停止,此时,会打印出容器的退出码
|
||
[root@qfedu.com ~]# docker wait 01d8aa //不同终端操作
|
||
137
|
||
|
||
diff
|
||
查看容器内发生改变的文件,以elated_lovelace容器为例
|
||
root@68656158eb8e:/# touch c.txt
|
||
|
||
```bash
|
||
用diff查看:
|
||
包括文件的创建、删除和文件内容的改变都能看到
|
||
[root@qfedu.com ~]# docker diff 容器名称
|
||
A /c.txt
|
||
|
||
C对应的文件内容的改变,A对应的均是文件或者目录的创建删除
|
||
[root@qfedu.com ~]# docker diff 7287
|
||
A /a.txt
|
||
C /etc
|
||
C /etc/passwd
|
||
A /run
|
||
A /run/secrets
|
||
```
|
||
|
||
宿主机和容器之间相互Copy文件
|
||
cp的用法如下:
|
||
docker cp [OPTIONS] CONTAINER:PATH LOCALPATH
|
||
docker cp [OPTIONS] LOCALPATH CONTAINER:PATH
|
||
如:容器mysql中/usr/local/bin/存在docker-entrypoint.sh文件,可如下方式copy到宿主机
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker cp mysql:/usr/local/bin/docker-entrypoint.sh /root
|
||
```
|
||
|
||
修改完毕后,将该文件重新copy回容器
|
||
|
||
```bash
|
||
[root@qfedu.com ~]# docker cp /root/docker-entrypoint.sh mysql:/usr/local/bin/
|
||
```
|
||
|
||
## 二、Docker容器镜像制作
|
||
|
||
### 1、容器文件系统打包
|
||
|
||
将容器的文件系统打包成tar文件,也就是把正在运行的容器直接导出为tar包的镜像文件
|
||
|
||
export Export a container's filesystem as a tar archive
|
||
|
||
有两种方式(elated_lovelace为容器名)
|
||
|
||
```shell
|
||
第一种:
|
||
[root@qfedu.com ~]# docker export -o elated_lovelace.tar elated_lovelace
|
||
第二种:
|
||
[root@qfedu.com ~]# docker export 容器名称 > 镜像.tar
|
||
```
|
||
|
||
导入镜像归档文件到其他宿主机
|
||
|
||
import Import the contents from a tarball to create a filesystem image
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker import elated_lovelace.tar elated_lovelace:v1
|
||
```
|
||
|
||
注意:
|
||
|
||
> 如果导入镜像时没有起名字,随后可以单独起名字(没有名字和tag),可以手动加tag
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker tag 镜像ID mycentos:7
|
||
```
|
||
|
||
### 2、通过容器创建本地镜像
|
||
|
||
背景:
|
||
|
||
容器运行起来后,又在里面做了一些操作,并且要把操作结果保存到镜像里
|
||
|
||
方案:
|
||
|
||
使用 docker commit 指令,把一个正在运行的容器,直接提交为一个镜像。commit 是提交的意思,类似告诉svn服务器我要生成一个新的版本。
|
||
|
||
例:在容器内部新建了一个文件
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker exec -it 4ddf4638572d /bin/sh
|
||
root@4ddf4638572d# touch test.txt
|
||
root@4ddf4638572d# exit
|
||
将这个新建的文件提交到镜像中保存
|
||
[root@qfedu.com ~]# docker commit 4ddf4638572d wing/helloworld:v2
|
||
```
|
||
|
||
例:
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker commit -m "my images version1" -a "wing" 108a85b1ed99 daocloud.io/ubuntu:v2
|
||
sha256:ffa8a185ee526a9b0d8772740231448a25855031f25c61c1b63077220469b057
|
||
-m 添加注释
|
||
-a 作者author
|
||
108a85b1ed99 容器环境id
|
||
daocloud.io/ubuntu:v2 镜像名称:hub的名称/镜像名称:tag
|
||
-p,–pause=true 提交时暂停容器运行
|
||
```
|
||
|
||
Init 层的存在,是为了避免执行 docker commit 时,把 Docker 自己对 /etc/hosts 等文件做的修改,也一起提交掉。
|
||
|
||
### 3、镜像迁移
|
||
|
||
保存一台宿主机上的镜像为tar文件,然后可以导入到其他的宿主机上:
|
||
|
||
save Save an image(s) to a tar archive
|
||
|
||
将镜像打包,与下面的load命令相对应
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker save -o nginx.tar nginx
|
||
[root@qfedu.com ~]# docker save > nginx.tar nginx
|
||
```
|
||
|
||
load Load an image from a tar archive or STDIN
|
||
|
||
与上面的save命令相对应,将上面sava命令打包的镜像通过load命令导入
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker load < nginx.tar
|
||
```
|
||
|
||
注:
|
||
|
||
1.tar文件的名称和保存的镜像名称没有关系
|
||
|
||
2.导入的镜像如果没有名称,自己打tag起名字
|
||
|
||
### 4、通过Dockerfile创建镜像
|
||
|
||
虽然可以自己制作 rootfs(见'容器文件系统那些事儿'),但Docker 提供了一种更便捷的方式,叫作 Dockerfile
|
||
|
||
docker build命令用于根据给定的Dockerfile和上下文以构建Docker镜像。
|
||
|
||
**docker build语法**
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker build [OPTIONS] <PATH | URL | ->
|
||
```
|
||
|
||
**选项说明**
|
||
|
||
> --build-arg,设置构建时的变量
|
||
>
|
||
> --no-cache,默认false。设置该选项,将不使用Build Cache构建镜像
|
||
>
|
||
> --pull,默认false。设置该选项,总是尝试pull镜像的最新版本
|
||
>
|
||
> --compress,默认false。设置该选项,将使用gzip压缩构建的上下文
|
||
>
|
||
> --disable-content-trust,默认true。设置该选项,将对镜像进行验证
|
||
>
|
||
> --file, -f,Dockerfile的完整路径,默认值为‘PATH/Dockerfile’
|
||
>
|
||
> --isolation,默认--isolation="default",即Linux命名空间;其他还有process或hyperv
|
||
>
|
||
> --label,为生成的镜像设置metadata
|
||
>
|
||
> --squash,默认false。设置该选项,将新构建出的多个层压缩为一个新层,但是将无法在多个镜像之间共享新层;设置该选项,实际上是创建了新image,同时保留原有image。
|
||
>
|
||
> --tag, -t,镜像的名字及tag,通常name:tag或者name格式;可以在一次构建中为一个镜像设置多个tag
|
||
>
|
||
> --network,默认default。设置该选项,Set the networking mode for the RUN instructions during build
|
||
>
|
||
> --quiet, -q ,默认false。设置该选项,Suppress the build output and print image ID on success
|
||
>
|
||
> --force-rm,默认false。设置该选项,总是删除掉中间环节的容器
|
||
>
|
||
> --rm,默认--rm=true,即整个构建过程成功后删除中间环节的容器
|
||
|
||
**PATH | URL | -说明**
|
||
|
||
给出命令执行的上下文。
|
||
|
||
docker build需要的各种文件必须放到上下文目录中
|
||
|
||
上下文可以是构建执行所在的本地路径,也可以是远程URL,如Git库、tar包或文本文件等。
|
||
|
||
如果是Git库,如https://github.com/docker/rootfs.git#container:docker,则隐含先执行git clone --depth 1 --recursive,到本地临时目录;然后再将该临时目录发送给构建进程。
|
||
|
||
构建镜像的进程中,可以通过ADD命令将上下文中的任何文件加入到镜像中。
|
||
|
||
-表示通过STDIN给出Dockerfile或上下文。
|
||
|
||
示例:
|
||
|
||
```shell
|
||
docker build - < Dockerfile
|
||
```
|
||
|
||
说明:该构建过程只有Dockerfile,没有上下文
|
||
|
||
```shell
|
||
docker build - < context.tar.gz
|
||
```
|
||
|
||
说明:其中Dockerfile位于context.tar.gz的根路径
|
||
|
||
可以同时设置多个tag
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker build -t champagne/bbauto:latest -t champagne/bbauto:v2.1 .
|
||
```
|
||
|
||
创建镜像所在的文件夹和Dockerfile文件
|
||
|
||
```shell
|
||
mkdir dockerfile-test
|
||
cd dockerfile-test
|
||
touch Dockerfile
|
||
```
|
||
|
||
在Dockerfile文件中写入指令,每一条指令都会更新镜像的信息例如:
|
||
|
||
```dockerfile
|
||
[root@qfedu.com ~]# vim Dockerfile
|
||
# This is a comment
|
||
FROM daocloud.io/library/centos:7
|
||
MAINTAINER wing wing@qfedu.com
|
||
RUN 命令1;命令2;命令3
|
||
RUN 命令
|
||
```
|
||
|
||
**格式说明**
|
||
|
||
> 每行命令都是以 INSTRUCTION statement 形式,就是命令+ 清单的模式。命令要大写,"#"是注解。
|
||
>
|
||
> FROM 命令是告诉docker 我们的镜像什么。
|
||
>
|
||
> MAINTAINER 是描述 镜像的创建人。
|
||
>
|
||
> RUN 命令是在镜像内部执行。就是说他后面的命令应该是针对镜像可以运行的命令。
|
||
|
||
**创建镜像**
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker build -t centos:v2 .
|
||
build 是docker创建镜像的命令
|
||
-t 镜像名称
|
||
"." 上下文目录,如果不用-f指定Dockerfile文件的位置和名称,默认会从上下文目录也就是这里指定的当前目录查找
|
||
```
|
||
|
||
创建完成后,用这个镜像运行容器
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker run -it centos:v2 /bin/bash
|
||
```
|
||
|
||
**--build-arg应用示例**
|
||
|
||
```shell
|
||
# cat Dockerfile
|
||
FROM daocloud.io/library/nginx:1.7.9
|
||
MAINTAINER wing wing@qq.com
|
||
ARG TEST
|
||
ARG TEST1
|
||
RUN echo $TEST > /a.txt
|
||
RUN echo $TEST1 > /b.txt
|
||
|
||
# docker build --build-arg TEST=123 --build-arg TEST1=456 -t nginx:v11 .
|
||
```
|
||
|
||
### 5、例01-容器化Python的Flask应用
|
||
|
||
**目标**
|
||
|
||
用 Docker 部署一个用 Python 编写的 Web 应用。
|
||
|
||
|
||
|
||
私有镜像仓库 OP
|
||
|
||
下载python RD
|
||
|
||
运行成容器 RD
|
||
|
||
写代码 RD
|
||
|
||
打包tar RD
|
||
|
||
上传tar包到私有仓库 RD
|
||
|
||
拉取带应用的镜像到测试服 OP
|
||
|
||
发版上线 OP
|
||
|
||
|
||
|
||
**应用代码部分**
|
||
|
||
代码功能:如果当前环境中有"NAME"这个环境变量,就把它打印在"Hello"后,否则就打印"Hello world",最后再打印出当前环境的 hostname。
|
||
|
||
```python
|
||
[root@qfedu.com ~]# mkdir python_app
|
||
[root@qfedu.com ~]# cd python_app
|
||
[root@qfedu.com ~]# vim app.py
|
||
from flask import Flask
|
||
import socket
|
||
import os
|
||
app = Flask(__name__)
|
||
@app.route('/')
|
||
def hello():
|
||
html = "<h3>Hello {name}!</h3><b>Hostname:</b> {hostname}<br/>"
|
||
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())
|
||
if __name__ == "__main__":
|
||
app.run(host='0.0.0.0', port=80)
|
||
```
|
||
|
||
**代码测试**
|
||
|
||
```bash
|
||
# yum search pip 检查是否有包
|
||
|
||
# yum install python2-pip
|
||
或者
|
||
# yum install python3-pip 安装python3
|
||
|
||
# pip3 install flask 下载flask模块
|
||
|
||
# python3 app.py 执行脚本
|
||
```
|
||
|
||
|
||
|
||
应用依赖
|
||
|
||
定义在同目录下的 requirements.txt 文件里,内容如下:
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# vim requirements.txt
|
||
Flask
|
||
|
||
|
||
```
|
||
|
||
编写Dockerfile
|
||
|
||
```dockerfile
|
||
[root@qfedu.com ~]# vim Dockerfile
|
||
FROM daocloud.io/library/python:2.7-slim
|
||
WORKDIR /app
|
||
ADD . /app
|
||
#RUN pip install --trusted-host pypi.python.org -r requirements.txt
|
||
RUN pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt
|
||
EXPOSE 80
|
||
ENV NAME World
|
||
# ENTRYPOINT ['/bin/sh','-c'] #新版本20.10.17的ENTRYPOINT有问题,可能会报找不到命令错误
|
||
CMD ["python", "app.py"]
|
||
```
|
||
|
||
![image-20200722153133293](assets/image-20200722153133293.png)
|
||
|
||
**Dockerfile文件说明**
|
||
|
||
FROM python:2.7-slim
|
||
|
||
使用官方提供的 Python 开发镜像作为基础镜像
|
||
|
||
指定"python:2.7-slim"这个官方维护的基础镜像,从而免去安装 Python 等语言环境的操作。否则,这一段就得这么写了:
|
||
|
||
FROM ubuntu:latest
|
||
|
||
RUN apt-get update -y
|
||
|
||
RUN apt-get install -y python-pip python-dev build-essential
|
||
|
||
WORKDIR /app
|
||
|
||
将工作目录切换为 /app,意思是在这一句之后,Dockerfile 后面的操作都以这一句指定的 /app 目录作为当前目录。
|
||
|
||
ADD . /app
|
||
|
||
将当前目录下的所有内容复制到 /app 下,Dockerfile 里的原语并不都是指对容器内部的操作。比如 ADD,指的是把当前目录(即 Dockerfile 所在的目录)里的文件,复制到指定容器内的目录当中。
|
||
|
||
RUN pip install --trusted-host pypi.python.org -r requirements.txt
|
||
|
||
使用 pip 命令安装这个应用所需要的依赖
|
||
|
||
EXPOSE 80 //允许外界访问容器的 80 端口
|
||
|
||
ENV NAME World //设置环境变量
|
||
|
||
|
||
|
||
CMD ["python", "app.py"]
|
||
|
||
设置容器进程为:python app.py,即:这个 Python 应用的启动命令
|
||
|
||
这里app.py 的实际路径是 /app/app.py。CMD ["python", "app.py"] 等价于 "docker run python app.py"。
|
||
|
||
在使用 Dockerfile 时,可能还会看到一个叫作 ENTRYPOINT 的原语。它和 CMD 都是 Docker 容器进程启动所必需的参数,完整执行格式是:"ENTRYPOINT CMD"。
|
||
|
||
但是,默认,Docker 会提供一个隐含的 ENTRYPOINT,即:/bin/sh -c。所以,在不指定 ENTRYPOINT 时,比如在这个例子里,实际上运行在容器里的完整进程是:/bin/sh -c "python app.py",即 CMD 的内容就是 ENTRYPOINT 的参数。
|
||
|
||
基于以上原因,后面会统一称 Docker 容器的启动进程为 ENTRYPOINT,而不是 CMD。
|
||
|
||
|
||
|
||
现在目录结构:
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# ls
|
||
Dockerfile app.py requirements.txt
|
||
```
|
||
|
||
**构建镜像**
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker build -t helloworld .
|
||
-t 给这个镜像加一个 Tag
|
||
```
|
||
|
||
Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语),它对应的层也会存在。只不过在外界看来,这个层是空的。
|
||
|
||
查看结果
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker image ls
|
||
REPOSITORY TAG IMAGE ID
|
||
helloworld latest 653287cdf998
|
||
```
|
||
|
||
启动容器
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker run -p 4000:80 helloworld
|
||
镜像名 helloworld 后面,什么都不用写,因为在 Dockerfile 中已经指定了 CMD。
|
||
否则,就得把进程的启动命令加在后面:
|
||
[root@qfedu.com ~]# docker run -p 4000:80 helloworld python app.py
|
||
```
|
||
|
||
查看容器
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker ps
|
||
CONTAINER ID IMAGE COMMAND CREATED
|
||
4ddf4638572d helloworld "python app.py" 10 seconds ago
|
||
```
|
||
|
||
进入容器
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker exec -it b69 /bin/bash
|
||
```
|
||
|
||
访问容器内应用
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# curl http://localhost:4000
|
||
```
|
||
|
||
<h3>Hello World!</h3><b>Hostname:</b> 4ddf4638572d<br/>
|
||
|
||
|
||
至此,已经使用容器完成了一个应用的开发与测试,如果现在想要把这个容器的镜像上传到 DockerHub 上分享给更多的人,首先要注册一个 Docker Hub 账号,然后使用 docker login 命令登录。
|
||
|
||
给容器镜像打tag起一个完整的名字:
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker tag helloworld yanqiang20072008/helloworld:v1
|
||
yanqiang20072008为我在docker hub的用户名
|
||
```
|
||
|
||
推送镜像到Docker Hub
|
||
|
||
```shell
|
||
[root@qfedu.com ~]# docker push yanqiang20072008/helloworld:v1
|
||
```
|
||
|
||
### 6、例02-部署docker官方文档
|
||
|
||
```bash
|
||
# docker run -d -p 8888:80 docker/getting-started
|
||
```
|
||
|
||
### 7、例03-部署node.js网站
|
||
|
||
**注意:构建镜像的过程非常缓慢,建议有时间的时候再尝试此实验**
|
||
|
||
访问上例中部署的网站下载app.zip压缩包
|
||
|
||
![image-20220719001900850](assets/image-20220719001900850.png)
|
||
|
||
文件已经下载到wing docker文档目录
|
||
|
||
在解压后的app目录中创建Dockerfile文件内容如下
|
||
|
||
```dockerfile
|
||
# syntax=docker/dockerfile:1
|
||
FROM node:12-alpine
|
||
RUN apk add --no-cache python g++ make
|
||
WORKDIR /app
|
||
COPY . .
|
||
RUN yarn install --production
|
||
CMD ["node", "src/index.js"]
|
||
```
|
||
|
||
构建镜像
|
||
|
||
```bash
|
||
注意:构建镜像的过程非常缓慢,建议有时间的时候再尝试此实验
|
||
# docker build -t getting-started .
|
||
```
|
||
|
||
启动容器
|
||
|
||
```bash
|
||
# docker run -dp 3000:3000 getting-started
|
||
```
|
||
|
||
访问应用
|
||
|
||
```
|
||
浏览器http://localhost:3000
|
||
```
|
||
|
||
![image-20210512150612818](assets/image-20210512150612818.png)
|
||
|
||
Update the source code
|
||
|
||
In the `src/static/js/app.js` file, update line 56 to use the new empty text.
|
||
|
||
```js
|
||
去掉此行<p className="text-center">No items yet! Add one above!</p>
|
||
添加本行<p className="text-center">You have no todo items yet! Add one above!</p>
|
||
```
|
||
|
||
Let’s build our updated version of the image, using the same command we used before.
|
||
|
||
```
|
||
# docker build -t getting-started .
|
||
```
|
||
|
||
Let’s start a new container using the updated code.
|
||
|
||
```
|
||
# docker run -dp 3000:3000 getting-started
|
||
```
|
||
|
||
查看结果
|
||
|
||
![image-20210512152020632](assets/image-20210512152020632.png)
|
||
|
||
### 8、例04-制作Kubectl的镜像
|
||
|
||
注:本实例学完k8s之后才能做
|
||
|
||
制作镜像:wing/kubectl
|
||
|
||
```dockerfile
|
||
[root@qfedu.com ~]#cat Dockerfile/kubectl/Dockerfile
|
||
FROM alpine
|
||
MAINTAINER wing <276267003@qq.com>
|
||
LABEL org.label-schema.vcs-ref=$VCS_REF \
|
||
org.label-schema.vcs-url="https://github.com/vfarcic/kubectl" \
|
||
org.label-schema.docker.dockerfile="/Dockerfile"
|
||
ENV KUBE_LATEST_VERSION="v1.13.0"
|
||
RUN apk add --update ca-certificates && \
|
||
apk add --update -t deps curl && \
|
||
curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl && \
|
||
chmod +x /usr/local/bin/kubectl && \
|
||
apk del --purge deps && \
|
||
rm /var/cache/apk/*
|
||
CMD ["kubectl", "help"]
|
||
```
|
||
|
||
### 9、Dockerfile优化
|
||
|
||
#### 控制镜像大小
|
||
|
||
编译一个简单的nginx成功以后发现好几百M。
|
||
|
||
1、RUN 命令要尽量写在一条里,每次 RUN 命令都是在之前的镜像上封装,只会增大不会减小
|
||
|
||
2、每次进行依赖安装后使用yum clean all清除缓存中的rpm头文件和包文件
|
||
|
||
3、选择比较小的基础镜像,比如:alpine
|
||
|
||
#### 使用构建缓存
|
||
|
||
Docker使用构建缓存可以显著加快Docker镜像的构建速度。当你在Dockerfile中执行指令时,Docker会检查它之前的指令是否已经执行过,并且是否存在缓存,如果存在缓存,Docker将重用缓存而不是重新执行指令。
|
||
|
||
以下是几个在Dockerfile中使用构建缓存的常用技巧:
|
||
|
||
1. 按顺序列出指令。将常用但不容易变更的指令,如`FROM`、`LABEL`、`ENV`等,放在Dockerfile的顶部,将变更频繁的指令,如`ADD`、`COPY`、`RUN`等,放在Dockerfile的底部。这样可以最大程度地利用缓存,减少不必要的重新构建。
|
||
2. 限制缓存范围。可以使用`COPY`指令的`--chown`参数或`RUN`指令的`--mount`参数来限制缓存的范围。例如,`COPY --chown=root:root file.txt /tmp`将限制缓存仅适用于该指令所复制的文件,而不适用于其他文件或目录。这样可以避免由于其他文件或目录的更改而不必要地重新构建。
|
||
3. 避免更新不必要的依赖项。在使用`RUN`指令安装依赖项时,可以指定版本号,以避免不必要的更新。例如,`RUN apt-get update && apt-get install -y python3=3.8.2-1ubuntu1`将只安装指定版本的Python3,而不是最新版本,从而节省构建时间。
|
||
4. 在需要清除缓存时使用`--no-cache`选项。有时,缓存可能会导致错误或意外的结果。在这种情况下,可以使用`docker build --no-cache`命令来禁用缓存,并从头开始重新构建Docker镜像。
|
||
|
||
以上是一些常见的使用构建缓存的技巧,可以帮助你优化Docker镜像的构建速度,提高效率。 |