08|视频:入门篇实操总结

你好,我是Chrono。

今天的课程和前面的不太一样,变成了视频的形式。之前也讲过很多次学习Kubernetes要以动手实操为主,加上专栏里单纯的文字配图的形式还是不太直观,所以每到一个学习阶段,我们就会来一个视频总结,把之前学习的内容以视频的形式展现出来,这样也许会让学习的效果更好。

这次视频课程的主要内容和第7讲差不多,是对“入门篇”的回顾与总结,但侧重点是对Docker的实际操作,不会再重复讲那些理论知识。每个视频后都会附上操作要点,供你快速定位和做笔记。

好了,我们正式开始吧。


一. 熟悉Docker的使用

视频操作要点:

首先来操作一下Docker Engine。

(有了课前准备的基础)在这台机器上,Docker已经安装好了,我给你用 docker versiondocker info 看一下它的信息。

docker version 显示的是Docker Engine 20.10.12,系统是Linux,硬件架构是arm64,也就是Apple M1。

docker info 显示的是当前系统相关的信息,例如CPU、内存、容器数量、镜像数量、容器运行时、存储文件系统等等。这里存储用的文件系统是overlay2,Linux内核是5.13,操作系统是Ubuntu 22.04 Jammy Jellyfish,硬件是aarch64,两个CPU,内存4G。

现在我们用 docker ps 看一下容器列表,应该是空的。

然后用 docker pull 拉取busybox镜像,再用 docker images 看镜像列表。

使用 docker run 启动busybox镜像,执行最简单的hello world:

docker run busybox echo hello world

然后再用 docker ps -a 查看已经结束的容器列表,应该可以看到刚刚运行完毕的Busybox容器,可以用 docker rm 再加上容器ID删除它。

二. 镜像和容器

视频操作要点:

我们再来拉取另一个镜像,操作系统Alpine:

docker pull alpine

然后用 docker run,加上it参数,运行它里面的shell:

docker run -it alpine sh

这样就暂时离开当前的Ubuntu操作系统,进入了容器内部的Alpine系统,可以在里面执行任意的命令,比如 cat /etc/os-release

这个容器环境与外面是完全隔离的,进程、文件系统都独立,不过也有没有隔离的部分,比如时间和内核。

使用exit退出容器,然后在宿主机环境执行date、uname -a,你就可以看到它与容器里是一致的。

让我们再运行一个容器:

docker run -d --rm nginx:alpine

在宿主机里用 ps -ef|grep nginx 可以看到有3个Nginx进程,它们其实就是容器里的Nginx进程,用docker stop停止后再用ps,就能发现它们已经消失了。

这就证明,容器其实就是操作系统里的进程,只是被容器运行环境加上了namespace、cgroup、chroot的限制,所以容器和普通进程在资源的使用方面是没有什么区别的,也因为没有虚拟机的成本,启动更迅速,资源利用率也就更高。

三. 构建自己的镜像

视频操作要点:

现在让我们来尝试编写Dockerfile,构建一个自己的镜像。

这个Dockerfile先用arg指令定义了IMAGE_BASE、IMAGE_TAG两个变量,然后使用from指令指定了构建的基础镜像,把这两个变量结合起来就是 nginx:1.21-alpine

后面的两个env指令定义了PATH和DEBUG两个环境变量。arg指令定义的变量只能在构建镜像的时候使用,而env定义的变量则会在容器运行的时候以环境变量的形式出现,让进程运行时使用。

接下来是copy指令,它会把构建上下文里的./default.conf拷贝到镜像的/etc/nginx/conf.d/,注意copy指令不能使用绝对路径,必须是构建上下文的相对路径,而且Docker会把构建上下文里的所有文件打包传递给docker daemon,所有尽量只包含必要的文件。

run指令就是构建镜像时要执行的shell命令,可以是安装软件、创建目录、编译程序等等,这里只是简单地用echo命令生成了一个文本文件。

最后两条指令是 exposeworkdirexpose 是声明容器对外服务的端口号,而 workdir 是容器的工作目录。

了解了Dockerfile的内容之后,我们就要用 docker build 来构建镜像了,使用 -t 打上标签,再加上构建上下文路径,当前目录就用一个点号 .

docker build -t ngx-app:1.0 .

构建完成,生成镜像文件,我们可以用 docker run 从镜像启动容器,验证镜像里的文件是否正确生成:

docker run -it --rm ngx-app:1.0 sh

然后我们还可以用 docker save/load 命令把它导出成压缩包,方便保存和传输:

docker save ngx-app:1.0 -o ngx.tar
docker load -i ngx.tar

四. 与外部系统互通的操作

视频操作要点:

接下来我们看看容器与外部系统互通的一些操作。

首先是 docker cp 命令。让我们先启动一个Redis容器:

docker run -d --rm redis

然后用 echo 命令生成一个简单的文本文件:

echo 'aaa' > a.txt

docker ps 命令看看容器的ID,就可以使用 docker cp 命令把这个文件拷贝进容器里了:

docker cp a.txt 062:/tmp

使用 docker exec 可以进入容器内部,查看文件是否已经正确拷贝:

docker exec -it 062 sh
ls /tmp

退出容器,我们再把这个文件改个名字,拷贝出来:

docker cp 062:/tmp/a.txt ./b.txt

现在我们再看看用 -v 参数直接挂载本地目录,把文件直接映射到容器内部:

docker run -d --rm -v /tmp:/tmp redis

docker exec 进入容器,查看一下容器内的“/tmp”目录,应该就可以看到文件与宿主机是完全一致的。

docker exec -it b5a sh    # b5a是容器ID

-p 参数是映射本机端口到容器端口,我们启动一个Nginx容器,把本机的80端口映射到容器的80端口:

docker run -d -p 80:80 --rm nginx:alpine

docker ps 可以看到端口的映射情况,我们也可以使用curl直接访问容器里的Nginx服务:

curl 127.1:80   -I

再使用exec就可以看到容器里的网卡情况:

docker exec xxx ip addr

可以发现容器里的网卡设置与宿主机完全不同,eth0是一个虚拟网卡,IP地址是B类私有地址“172.17.0.2”。

五. 搭建WordPress

视频操作要点:

最后演示一下使用Docker搭建WordPress的过程。

因为在Docker命令里写环境变量很麻烦,命令很长,所以我把搭建的过程写成了一个脚本。

一共有三条命令,首先启动MariaDB,设置数据库名、用户名、密码等环境变量,然后启动WordPress,使用刚才的MariaDB的用户名、密码,db_host必须是MariaDB的IP地址,然后再启动Nginx,它需要在配置文件里指定WordPress的地址,然后用-v参数挂载进容器里。

执行这个脚本之后,我们用 docker ps 看一下容器的状态。

确认容器都运行正常,我们就可以在浏览器里输入IP地址,访问WordPress网站了。

课下作业

今天是动手操作课,作业就是一定记得让自己实际上手操作一遍哦。

欢迎在留言区分享自己的实操感受,如果有什么疑问也欢迎留言分享参与讨论。我们下节课初级篇见。

精选留言

  • Geek_4d5ba0

    2022-07-27 12:02:45

    老师讲的太赞了,一个上午看完入门篇,其中的细节讲的特别好,容易理解,像--rm,rm,rmi简直了,把删除镜像,删除容器,删除未运行状态的容器讲的一清二楚,还有容器仓库也是,mariadb这个数据库名字都讲到了。期待老师的后续课程
    作者回复

    thanks

    2022-07-27 16:38:20

  • lesserror

    2022-07-09 23:18:54

    老师,真不打算讲讲 volumes 相关的内容吗? 我看这在docker里面好像属于比较核心的内容。

    另外,怎么理解:dangling images 这种现象呢?
    作者回复

    毕竟我们的目标是学习Kubernetes,以后有机会可以专门讲docker。

    简单理解,有<none>标记,没标签的就是dangling images,一般是标签被新镜像覆盖了。

    2022-07-10 07:04:47

  • lesserror

    2022-07-08 22:31:42

    老师文中有这么一句话:“构建完成,生成镜像文件,我们可以用 docker run 进入镜像,验证镜像里的文件是否正确生成。”

    这里使用docker run 进入的是容器还是镜像? 我理解应该是进入容器里面吧?
    作者回复

    sorry,表述不准确,是用run从镜像启动容器。

    2022-07-09 15:58:48

  • Magic

    2022-07-08 10:30:14

    实践出真知,期待老师的下一节课。
    作者回复

    great

    2022-07-08 10:48:50

  • Reiser

    2022-07-08 10:24:38

    这种边学边动手的方式太棒啦!
    作者回复

    thanks

    2022-07-08 10:48:54

  • 麻婆豆腐

    2022-07-17 07:29:38

    老师好,启动容器后修改了容器里的内容,比如网数据库里写入了数据。容器退出后会自动保存吗。容器有快照吗,可以回滚到某个时间节点。
    作者回复

    容器不会保存任何东西,强调的就是用完就扔,想要保存数据就要外挂存储卷。

    2022-07-17 21:19:25

  • 一颗红心

    2022-07-08 17:57:12

    期待更多实战内容 ^_^
    作者回复

    thanks for your support.

    2022-07-08 20:58:01

  • peter

    2022-07-08 10:05:15

    请教老师几个问题啊:
    Q1:docker info的输出中,“存储文件系统”是指宿主机的文件系统吗? 还是指docker内部的文件系统? 我感觉应该是docker内部的文件系统。 我用 df –T 命令查询了宿主机的文件系统,并没有发现“overlay2”。
    Q2:docker的内核必须与宿主机一样吗?
    在容器内核宿主机上用uname –a查到的结果必须一样吗?即宿主机和docker的内核必须一样吗?
    宿主机的查询结果:
    Linux peter-vm3 5.15.0-40-generic #43-Ubuntu SMP Wed Jun 15 12:54:21 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
    容器中的查询结果:
    Linux 60620d76d4f6 5.15.0-40-generic #43-Ubuntu SMP Wed Jun 15 12:54:21 UTC 2022 x86_64 Linux
    Q3:docker与宿主机的date命令查询结果必须一样吗?
    我的查询结果是两个时间是不同的,如下所示:
    宿主机上:022年 07月 08日 星期五 09:48:46 CST
    容器内部:Fri Jul 8 01:48:27 UTC 2022
    作者回复


    1. 这个overlay2是union fs文件系统,和Linux的ext3、ext不一样,可以理解成是一种实现技术。

    2.uname看内核版本,都是5.15,前面的是主机名。

    3.时区不一样,一个是cst,一个是utc。

    2022-07-08 11:24:29

  • 密码123456

    2022-07-08 09:19:40

    *⁂((✪⥎✪))⁂* 很不错,虽然之前都敲了一遍。安装仓库的时候,我这边推不成功,网上说,需要把仓库的协议从https改为http才能推送成功,后来改了一下,果然可以了。不知道有同鞋有没有遇到和我一样的问题
    作者回复

    如果是docker hub好像不需要什么改动,Registry开什么协议需要看一下。

    2022-07-08 10:51:34

  • 美妙的代码

    2022-07-08 08:45:22

    这个安排好啊,以后k8s 操作起来方便了
    作者回复

    great

    2022-07-08 10:50:23

  • Geek_d3f1e0

    2023-05-16 16:09:13

    没有docker基础,重新入门学习了docker,回顾了两边,终于按照课程把WordPress跑了起来。
    作者回复

    great

    2023-05-17 10:07:56

  • 马晓文

    2023-03-14 20:01:26

    # Dockerfile
    # docker build -t ngx-app .
    # docker build -t ngx-app:1.0 .

    ARG IMAGE_BASE="nginx"
    ARG IMAGE_TAG="1.21-alpine"

    FROM ${IMAGE_BASE}:${IMAGE_TAG}


    ENV PATH=$PATH:/tmp
    ENV DEBUG=OFF

    COPY ./default.conf /etc/nginx/conf.d/

    RUN cd /usr/share/nginx/html \
    && echo "hello nginx" > a.txt

    EXPOSE 8081 8082 8083

    WORKDIR /etc/nginx

    git 地址:https://github.com/chronolaw/k8s_study
    作者回复

    good

    2023-03-15 10:57:05

  • syz

    2022-11-12 10:45:05

    在宿主机里用 ps -ef|grep nginx 可以看到有 3 个 Nginx 进程,它们其实就是容器里的 Nginx 进程,用 docker stop 停止后再用 ps,就能发现它们已经消失了。
    结果:没有看到3个nginx进程;
    ------
    docker run -d --rm nginx:alpine
    63b08ca159763cf77dce32558cebedcad5716e8dd1177118770883303741c7d3
    syz@localhost  ~  docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    63b08ca15976 nginx:alpine "/docker-entrypoint.…" 11 seconds ago Up 9 seconds 80/tcp kind_keldysh
    syz@localhost  ~  ps -ef|grep nginx
    501 5942 5137 0 10:41上午 ttys000 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox nginx
    syz@localhost  ~  docker info
    Client:
    Context: default
    Debug Mode: false
    Plugins:
    buildx: Build with BuildKit (Docker Inc., v0.6.1-docker)
    compose: Docker Compose (Docker Inc., v2.0.0-rc.3)
    scan: Docker Scan (Docker Inc., v0.8.0)

    Server:
    Containers: 2
    Running: 1
    Paused: 0
    Stopped: 1
    Images: 3
    Server Version: 20.10.8
    Storage Driver: overlay2
    Backing Filesystem: extfs
    Supports d_type: true
    Native Overlay Diff: true
    userxattr: false
    Logging Driver: json-file
    Cgroup Driver: cgroupfs
    Cgroup Version: 1
    Plugins:
    Volume: local
    Network: bridge host ipvlan macvlan null overlay
    Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
    Swarm: inactive
    Runtimes: io.containerd.runtime.v1.linux runc io.containerd.runc.v2
    Default Runtime: runc
    Init Binary: docker-init
    containerd version: e25210fe30a0a703442421b0f60afac609f950a3
    runc version: v1.0.1-0-g4144b63
    init version: de40ad0
    Security Options:
    seccomp
    Profile: default
    Kernel Version: 5.10.47-linuxkit
    Operating System: Docker Desktop
    OSType: linux
    Architecture: x86_64
    CPUs: 6
    Total Memory: 1.939GiB
    Name: docker-desktop
    ...
    作者回复

    看到这里用的是docker desktop,也许是环境的问题,换成Linux + docker Engine再看看。

    2022-11-13 07:54:47

  • 张申傲

    2022-09-13 20:09:16

    请教下老师,Dockerfile 中的 RUN 和 CMD 的作用和区别分别是什么呢?在很多地方看到这两种方式都是用来执行容器命令的。
    作者回复

    在第4讲里应该已经说得比较清楚了,如果有不清楚再具体提问。

    2022-09-14 06:57:44

  • 大梧桐树

    2022-07-25 20:20:45

    nginx的proxy_pass为啥指向172.17.0.3?这个内网地址nginx容器能访问到吗?
    作者回复

    它们都在docker的内网环境里,当然可以访问,172.17.0.3是通过docker inspect看到的WordPress的IP地址。

    2022-07-26 06:50:48

  • Fan

    2022-07-14 23:21:29

    老师,能否讲讲docker 数据卷,数据持久化呢?
    作者回复

    这个和Kubernetes的有重叠,我们专栏的目标是Kubernetes,所以docker不可能面面俱到,抱歉了。

    2022-07-15 06:59:59

  • Fan

    2022-07-13 23:56:55

    有视频真赞。
    作者回复

    thanks

    2022-07-14 06:16:48

  • 纳兰容若

    2022-07-11 10:34:47

    老师您好
    咱们这门课也和其他课程一样在github上有课程的资料和代码么
    作者回复

    是的,看课前准备,https://github.com/chronolaw/k8s_study。

    2022-07-11 11:19:33

  • lesserror

    2022-07-10 17:46:12

    老师,我看有的Dockerfile文件中是这么写的,能帮我分析一下分别代表的意思吗?

    COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]

    COPY . .

    USER node
    作者回复

    就是copy的表现形式不一样,可以按照Linux的cp命令来理解。

    其他命令可以参考docker官网文档。

    2022-07-11 10:29:50

  • pyhhou

    2022-07-10 13:59:55

    老师,我这边 docker info 输出的操作系统(Operating System)是 Ubuntu 22.04 LTS,和你视频中显示的不一样。因为我是用你在第 2 讲中上传的 Apple M1 Ubuntu 镜像来安装的,按理说应该和你视频中是一样的操作系统才对吧?

    想到你之前说过最新的 Ubuntu 对 Apple M1 会有一些兼容性的问题,不知道对接下来的课程环境搭建会不会有影响?到目前为止,用的倒是没啥问题
    作者回复

    只要使用正常就没问题,不需要和课程里一模一样。

    2022-07-11 10:27:18