赞
踩
而Docker确巧妙的解决了这些问题,那么Docker是如何实现的呢?
Docker为了解决依赖的兼容问题,采用了两个手段
容器
去运行,避免相互干扰这样打包好的应用中,既包含了应用本身,也包含了应用所需要用到的函数库和依赖,无序在操作系统上安装这些,自然也就不存在不同应用之间的兼容问题了
虽然解决了不同应用的兼容问题,但是开发、测试等环节会存在差异,操作系统版本也会有差异,这些问题又该如何解决呢?
要解决不同操作系统环境差异问题,必须先了解操作系统结构,以一个Ubuntu操作系统为例,结构如下
应用于计算机交互的流程如下
Ubuntu和CentOS都是基于Linux内核,无非是系统应用不同,提供的函数库有差异
此时,如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或不匹配,就会报错
Docker如何解决不同系统环境的问题?
打包
,形成可移植镜像隔离
Docker可以让一个应用在任何操作系统中都十分方便的运行,而我们以前接触的虚拟机,也能在一个操作系统中,运行另外一个操作系统,保护系统中的任何应用
二者有什么差异呢?
模拟
硬件设备,然后运行另一个操作系统。例如在Windows系统中运行CentOS系统,就可以运行任意的CentOS应用了特性 | Docker | 虚拟机 |
---|---|---|
性能 | 接近原生 | 性能较差 |
硬盘占用 | 一般为MB | 一般为GB |
启动 | 秒级 | 分钟级 |
镜像(Image)
:Docker将应用程序及其所需要的依赖、函数库、环境、配置等文件打包在一起,称为镜像容器(Container)
:镜像中的应用程序形成后的进程就是容器
,只是Docker会给容器进程做隔离,对外不可见镜像
,就是吧一个应用在硬盘上的文件、机器运行环境、部分系统函数库文件一起打包成的文件包。这个文件包是只读的(防止你对镜像文件进行修改/污染,从而导致镜像不可用,容器从镜像中拷贝一份文件到自己的空间里来写数据)容器
呢,就是把这些文件中编写的程序、函数加载到内存中允许形成进程,只不过要隔离起来。因此一个镜像可以启动多次,形成多个容器进程。开源应用程序非常多,打包这些应用往往都是重复性劳动,为了避免这些重复劳动,人们就会将自己打包的应用镜像,例如Redis、MySQL镜像放到网络上来共享使用,就像GitHub的代码共享一样
我们一方面可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像
镜像:
容器:
Docker结构:
DockerHub:
Docker 分为 CE 和 EE 两大版本。CE 即社区版,免费,支持周期 7 个月;EE 即企业版,强调安全,付费使用,支持周期 24 个月。
Docker CE 分为 stable
test
和 nightly
三个更新频道。
官方网站上有各种环境下的 安装指南,这里主要介绍 Docker CE 在 CentOS上的安装。
Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10
{% note warning no-icon %}
CentOS 7满足最低内核要求,本文也是在CentOS 7安装Docker
{% endnote %}
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
# 设置Docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
yum install -y docker-ce
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
# 启动docker服务
systemctl start docker
# 停止docker服务
systemctl stop docker
# 重启docker服务
systemctl restart docker
docker -v
结果
[root@localhost ~]# docker -v
Docker version 20.10.21, build baeda1f
mysql:5.7
,这里的mysql就是repository,5.7就是tag,合在一起就是镜像名称,代表5.7版本的MySQL镜像mysql:latest
首先我们取镜像仓库(例如DockerHub)中搜索Nginx镜像
根据查看到的镜像名称,拉取自己需要的镜像,通过命令:docker pull nginx
拉取最新的nginx镜像
[root@localhost ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
025c56f98b67: Pull complete
ec0f5d052824: Pull complete
cc9fb8360807: Pull complete
defc9ba04d7c: Pull complete
885556963dad: Pull complete
f12443e5c9f7: Pull complete
Digest: sha256:75263be7e5846fc69cb6c42553ff9c93d653d769b94917dbda71d42d3f3c00d3
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
从日志中我们也可以看出,如果不加tag,用的就是默认的latest,也就是拉取最新的docker镜像
docker images
查看拉取到的镜像[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 3964ce7b8458 4 days ago 142MB
docker save
将nginx镜像导出磁盘,然后在通过docker load
加载回来docker xx --help
命令查看docker save
和docker load
的语法docker save --help
,结果如下[root@localhost ~]# docker save --help
Usage: docker save [OPTIONS] IMAGE [IMAGE...]
Save one or more images to a tar archive (streamed to STDOUT by default)
Options:
-o, --output string Write to a file, instead of STDOUT
命令格式:
docker save -o [保存的目标文件名称] [镜像名称]
docker load --help
,结果如下[root@localhost ~]# docker load --help
Usage: docker load [OPTIONS]
Load an image from a tar archive or STDIN
Options:
-i, --input string Read from tar archive file, instead of STDIN
-q, --quiet Suppress the load output
命令格式:
docker load -i [镜像压缩文件名]
{% endnote %}
docker save -o nginx.tar nginx:latest
docker rmi nginx:latest # rmi是remove image的缩写
随后运行命令,加载本地文件
docker load -i nginx.tar
{% note info no-icon %}
需求:去DockerHub中搜索并拉取一个Redis镜像
docker pull
命令拉取镜像docker save
命令,将redis:latest
打包成一个redis.tar
包docker rmi
删除本地的redis:latest
docker load
重新加载redis.tar
文件容器操作命令如图
容器保护三个状态
暂停和停止的操作系统的处理方式不同,暂停是操作系统将容器内的进程挂起,容器关联的内存暂存起来,然后CPU不再执行这个进程,但是使用unpause
命令恢复,内存空间被恢复,程序继续运行。
停止是直接将进程杀死,容器所占的内存回收,保存的仅剩容器的文件系统,也就是那些静态的资源
docker rm
是将文件系统也彻底删除,也就是将容器彻底删除掉了
{% endnote %}
docker run
:创建并运行一个容器,处于运行状态
docker pause
:让一个运行的容器暂停
docker unpause
:让一个容器从暂停状态恢复运行
docker stop
:停止一个运行的容器
docker start
:让一个停止的容器再次运行
docker rm
:删除一个容器
docker run --name containerName -p 80:80 -d nginx
命令解读
docker run
:创建并运行一个容器--name
:给容器起一个名字,例如叫做myNginx-p
:将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口-d
:后台运行容器nginx
:镜像名称,例如nginx这里的-p
参数,是将容器端口映射到宿主机端口
默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx
现在,容器的80端口和宿主机的80端口关联了起来,当我们访问宿主机的80端口时,就会被映射到容器的80端口,这样就能访问nginx了
那我们再浏览器输入虚拟机ip:80就能看到nginx默认页面了
{% note info no-icon %}
需求:进入Nginx容器,修改HTML文件内容,添加Welcome To My Blog!
提示:进入容器要用到docker exec
命令
[root@localhost ~]# docker exec --help Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] Run a command in a running container Options: -d, --detach Detached mode: run command in the background --detach-keys string Override the key sequence for detaching a container -e, --env list Set environment variables --env-file list Read in a file of environment variables -i, --interactive Keep STDIN open even if not attached --privileged Give extended privileges to the command -t, --tty Allocate a pseudo-TTY -u, --user string Username or UID (format: <name|uid>[:<group|gid>]) -w, --workdir string Working directory inside the container
{% endnote %}
docker exec -it myNginx bash
docker exec
:进入容器内部,执行一个命令-it
:给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互myNginx
:要进入的容器名称bash
:进入容器后执行的命令,bash是一个linux终端交互命令进入nginx的HTML所在目录
/usr/share/nginx/html
cd /usr/share/nginx/html
查看目录下的文件
root@310016c9b413:/usr/share/nginx/html# ls
50x.html index.html
修改index.html的内容
sed -i -e 's#Welcome to nginx#Welcome To My Blog#g' index.html
在浏览器访问自己的虚拟机ip:80,即可看到结果(80端口可以不写)
docker run
命令常见的参数有哪些?
--name
:指定容器名称-p
:指定端口映射-d
:让容器后台运行查看容器日志的命令
docker logs
-f
参数可以持续查看日志查看容器状态:
docker ps
docker ps -a
查看所有容器,包括已停止的{% note info no-icon %}
现在是不是感觉修改文件好麻烦,因为没给提供vi命令,不能直接编辑,所以这就要用到我们下面说的数据卷了
{% endnote %}
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录
一旦完成数据卷挂载,对容器的一切操作都会作用在对应的宿主机目录了。这样我们操作宿主机的/var/lib/docker/volumes/html目录,就等同于操作容器内的/usr/share/nginx/html目录了
docker volume [COMMAND]
docker volume
命令是数据卷操作,根据命令后跟随的command
来确定下一步的操作
create
:创建一个volumeinspect
:显示一个或多个volume的信息ls
:列出所有的volumeprune
:删除未使用的volumerm
:删除一个或多个指定的volume{% note info no-icon %}
需求:创建一个数据卷,并查看数据卷在宿主机的目录位置
{% endnote %}
docker volume create html
docker volume ls
结果
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local html
docker volume inspect html
结果
[root@localhost ~]# docker volume inspect html
[
{
"CreatedAt": "2022-12-19T12:51:54+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/html/_data",
"Name": "html",
"Options": {},
"Scope": "local"
}
]
可以看到我们创建的html这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data
docker volume create
:创建数据卷docker volume ls
:查看所有数据卷docker volume inspect
:查看数据卷详细信息,包括关联的宿主机目录位置docker volume rm
:删除指定数据卷rocker volume prune
:删除所有未使用的数据卷docker run \
-- name myNginx \
-v html:/root/html \
-p 8080:80 \
nginx \
-v html:/root/html
:把html数据卷挂载到容器内的/root/html这个目录中{% note info no-icon %}
需求:创建一个nginx容器,修改容器内的html
目录的index.html
内容
分析:上个案例中,我们进入nginx容器内部,已经知道了nginx的html目录所在位置/usr/share/nginx/html
,我们需要把这个目录挂载到html
这个数据卷上,方便操作其中的内容
提示:运行容器时,使用-v
参数挂载数据卷
{% endnote %}
docker run --name myNginx -v html:/usr/share/nginx/html -p 80:80 -d nginx
# 查看数据卷位置
docker volume inspect html
# 进入该目录
cd /var/lib/docker/volumes/html/_data
# 修改文件
vi index.html
# 也可以在FinalShell中使用外部编译器(例如VSCode)来修改文件
容器不仅仅可以挂载数据卷,也可以直接挂载到宿主机目录上,关系如下
目录挂载和数据卷挂载的语法是类似的
{% note info no-icon %}
需求:创建并运行一个MySQL容器,将宿主机目录直接挂载到容器
{% endnote %}
docker pull mysql
/tmp/mysql/data
mkdir -p /tmp/mysql/data
/tmp/mysql/conf
,将myCnf.cnf
文件上传到/tmp/mysql/conf
mkdir -p /tmp/mysql/conf
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
{% endtabs %}
4. 去DockerHub中查阅资料,找到mysql容器内的conf目录和data目录的位置
容器中conf目录的位置是:/etc/mysql/conf.d
容器中存储数据的目录为:/var/lib/mysql
5. 创建并运行MySQL容器,要求
- 挂载/tmp/mysql/data到mysql容器内数据存储目录
- 挂载/tmp/mysql/conf/myCnf.cnf到mysql容器的配置文件
- 设置MySQL密码
BASH docker run \ --name mysql \ -e MYSQL_ROOT_PASSWORD=root \ -v /tmp/mysql/conf:/etc/mysql/conf.d \ -v /tmp/mysql/data:/var/lib/mysql \ -p 3306:3306 \ -d \ mysql
6. 尝试使用Navicat连接数据库,注意自己设置的密码
docker run
的命令中通过-v参数挂载文件或目录到容器中
-v [volume名称]:[容器内目录]
-v [宿主机文件]:[容器内文件]
-v [宿主机目录]:[容器内目录]
数据卷挂载与目录直接挂载的区别
镜像是将应用程序及其需要的系统函数库,环境、配置、依赖打包而成
以MySQL为例,来看看它的镜像组成结构
简单来说,镜像就是在系统函数库、运行环境的基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件
我们要构建镜像,其实就是实现上述打包的过程
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:6 |
ENV | 设置环境变量,可在后面指令使用 | ENV key value |
COPY | 拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm /tmp |
RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN yum install gcc |
EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINTjava -jar xxjar |
{% note info no-icon %}
需求:基于Ubuntu镜像构建一个新镜像,运行一个Java项目
{% endnote %}
mkdir /tmp/docker-demo
# 指定基础镜像 FROM ubuntu:16.04 # 配置环境变量,JDK的安装目录 ENV JAVA_DIR=/usr/local # 拷贝jdk的到JAVA_DIR目录下 COPY ./jdk8.tar.gz $JAVA_DIR/ # 安装JDK RUN cd $JAVA_DIR && tar -xf ./jdk8.tar.gz && mv ./jdk1.8.0_44 ./java8 # 配置环境变量 ENV JAVA_HOME=$JAVA_DIR/java8 ENV PATH=$PATH:$JAVA_HOME/bin # 拷贝java项目的包到指定目录下,我这里是/tmp/app.jar COPY ./docker-demo.jar /tmp/app.jar # 暴露端口,注意这里是8090端口,如果你之前没有关闭防火墙,请关闭防火墙或打开对应端口,云服务器同理 EXPOSE 8090 # 入口,java项目的启动命令 ENTERPOINT java -jar /tmp/app.jar
docker build
命令构建镜像docker build -t docker_demo:1.0 .
[root@localhost docker-demo]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker_demo 1.0 c8acd2dd02cf About a minute ago 722MB
redis latest 29ab4501eac3 2 days ago 117MB
nginx latest 3964ce7b8458 5 days ago 142MB
ubuntu 16.04 b6f507652425 15 months ago 135MB
mysql 5.7.25 98455b9624a9 3 years ago 372MB
docker run --name testDemo -p 8090:8090 -d docker_demo:1.0
{% note info no-icon %}
需求:基于java:8-alpine镜像,将一个Java项目构建为镜像
{% endnote %}
/tmp/docker-demo
目录)# 将openjdk:8作为基础镜像
FROM openjdk:8
# 拷贝java项目的包到指定目录下,我这里是/tmp/app.jar
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口
ENTRYPOINT java -jar /tmp/app.jar
docker build -t docker_demo:2.0 .
docker run --name testDemo02 -p 8090:8090 -d docker_demo:2.0
version: "3.8" services: # docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -v /tmp/mysql/data:/var/lib/mysql -v /tmp/mysql/conf/myCnf.cf:/etc/mysql/conf.d/myCnf.cnf -d mysql:5.7.25 mysql: # 对应docker run中的 --name image: mysql:5.7.25 # 对应docker run中最后声明的镜像 enviroment: # 对应docker run中的 -e MYSQL_ROOT_PASSWIRD=root MYSQL_ROOT_PASSWORD: root volumes: # 对应docker run中的 -v /tmp/mysql/data:/var/lib/mysql - "/tmp/mysql/data:/var/lib/mysql" - "/tmp/mysql/conf/myCnf.cf:/etc/mysql/conf.d/myCnf.cnf" # 这里并不需要-d参数来后台运行,因为此种方法默认就是后台运行 # 同时也不需要暴露端口,在微服务集群部署中,MySQL仅仅是供给给集群内的服务使用的,所以不需要对外暴露端口 # 临时构建镜像并运行,下面的配置文件包含了docker build和docker run两个步骤 # docker build -t web:1.0 . # docker run --name web -p 8090:8090 -d web:1.0 web: build: . ports: - "8090:8090"
mysql
:一个基于mysql:5.7.25
镜像构建的容器,并且挂载了两个项目web
:一个基于docker build
临时构建的镜像容器,映射端口为8090
# 安装
curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
如出现错误Failed connect to raw.githubusercontent.com:443; Connection refused
,需要修改自己的hosts文件
echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts
如出现新错误TCP connection reset by peer
,重复发起命令,多试几次
{% note info no-icon %}
需求:将之前学习的cloud-demo微服务集群利用DockerCompose部署
{% endnote %}
docker-compose up -d
来部署version: "3.2" services: nacos: image: nacos/nacos-server environment: MDOE: standalone ports: - "8848:8848" mysql: image: mysql:5.7.25 environment: MYSQL_ROOT_PASSWORD: root volumes: - "$PWD/mysql/data:/var/lib/mysql" # 这里的$PWD是执行linux命令,获取当前目录 - "$PWD/mysql/conf:/etc/mysql/conf.d" userservice: build: ./user-service orderservice: build: ./order-service gateway: build: ./gateway poets: - "10010:10010"
spring:
cloud:
nacos:
# server-addr: localhost:80 #Nacos地址
server-addr: nacos:8848 # 使用compose中的服务名来互相访问,用nacos替换localhost
config:
file-extension: yaml # 文件后缀名
server:
port: 8081
spring:
datasource:
# url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false
url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false # 这里同理,使用mysql替换localhost
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
{% endtabs %}
<build>
<!-- 服务打包的最终名称 -->
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
{% tabs 编写三个dockerfile %}
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar
{% endtabs %}
docker-compose up -d
com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance/list after all servers([nacos:8848]) tried: java.net.ConnectException: Connection refused (Connection refused)
docker-compose logs -f
阿里巴巴nacos连接失败,其原因是userservice在nacos之前启动了,而nacos启动太慢了,userservice注册失败,而且也没有重试机制(等nacos启动完成后,重试注册,就可以避免这个问题)
docker-compose restart gateway userservice orderserivce
docker-compose logs -f userservice
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://192.168.128.101:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=Kyle's Blog私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
docker tag nginx:latest 192.168.128.130:8080/nginx:1.0
docker push 192.168.128.130:8080/nginx:1.0
docker pull 192.168.128.130:8080/nginx:1.0
{% link 什么是Docker–程序员小灰, https://zhuanlan.zhihu.com/p/187505981, https://static.zhihu.com/heifetz/favicon.ico%}
作为程序员我们应怎样理解docker?
容器技术的起源
假设公司正在秘密研发下一个“今日头条”APP,我们姑且称为明日头条,程序员自己从头到尾搭建了一套环境开始写代码,写完代码后程序员要把代码交给测试同学测试,这时测试同学开始从头到尾搭建这套环境,测试过程中出现问题程序员也不用担心,大可以一脸无辜的撒娇,“明明在人家的环境上可以运行的”。
测试同学测完后终于可以上线了,这时运维同学又要重新从头到尾搭建这套环境,费了九牛二虎之力搭建好环境开始上线,糟糕,上线系统就崩溃了,这时心理素质好的程序员又可以施展演技了,“明明在人家的环境上可以运行的”。
从整个过程可以看到,不但我们重复搭建了三套环境还要迫使程序员转行演员浪费表演才华,典型的浪费时间和效率,聪明的程序员是永远不会满足现状的,因此又到了程序员改变世界的时候了,容器技术应运而生。
有的同学可能会说:“等等,先别改变世界,我们有虚拟机啊,VMware好用的飞起,先搭好一套虚拟机环境然后给测试和运维clone出来不就可以了吗?”
在没有容器技术之前,这确实是一个好办法,只不过这个办法还没有那么好。
那么这个办法为什么不好
呢?
因为操作系统太笨重
了,操作系统运行起来是需要占用很多资源的,大家对此肯定深有体会,刚装好的系统还什么都没有部署,单纯的操作系统其磁盘占用至少几十G起步,内存要几个G起步。
我们需要的只是一个简单的应用程序,不需要把内存浪费在对我们应用程序无用
的操作系统上。
此外,还有启动时间的问题,我们知道操作系统重启是非常慢的,因为操作系统要从头到尾把该检测的都检测了,该加载的都加载上,这个过程非常缓慢,动辄就得几分钟,所以操作系统终究还是太笨重了
那么有没有一种技术可以让我们获得虚拟机的好处又能克服这些缺点从而一举实现鱼和熊掌的兼得
呢?
容器技术。
什么是容器
那么我们该怎么使用容器呢?
这就要讲到docker了。
什么是docker
如何使用docker
docker中有这样几个概念:
实际上你可以简单的把image理解为可执行程序,container就是运行起来的进程。
那么写程序需要源代码,那么“写”image就需要dockerfile,dockerfile就是image的源代码,docker就是"编译器"。
因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。
具体的使用方法就不再这里赘述了,大家可以参考docker的官方文档,那里有更详细的讲解。
docker是如何工作的
接下来我们用几个命令来讲解一下docker的工作流程:
最后,让我们来看一下docker的底层实现
。
总结
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。