Docker入门

工欲善其事,必先利其器。

最近想在本地搭建Mysql主备、集群环境。之前的做法要么是本地起多个实例,绑定不同的端口;要么是创建多个虚拟机,但虚拟机资源占用高,搭建效率低。目前更轻量快速的方案是使用Docker,在Docker官网上有一个分为6章的《Get started with Docker》文档。本文基于文档整理了环境搭建过程以及中间涉及到的各个概念。

启动第一个Docker容器

我的本地环境是Mac OS,选择的Docker安装包是Docker for Mac (macOS)。安装完成后,可以通过以下命令测试Docker是否安装成功。

➜  ~ docker --version
Docker version 18.09.0, build 4d60db4

Docker文档中给出的示例镜像是:hello-world。通过docker run命令启动第一个Docker容器。

➜  ~ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.
...
For more examples and ideas, visit:
 https://docs.docker.com/get-started/

这里涉及到两个概念:镜像和容器。目前可以先简单的将镜像理解为Java语言中的类,将容器理解为对象。Docker通过镜像创建容器,一个镜像可以创建多个容器

通过docker image ls可以查看本地的镜像:

➜  ~ docker image ls
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
hello-world                        latest              4ab4c602aa5e        3 months ago        1.84kB

通过docker container ls -a可以查看容器:

➜  ~ docker container ls -a
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS                     PORTS               NAMES
541e363b4097        hello-world                             "/hello"                 7 minutes ago       Exited (0) 7 minutes ago                       zen_swanson

541e363b4097 就是上面通过docker run hello-world启动的容器。

如何定义一个自己的镜像

这里需要先介绍一下Dockerfile。Dockerfile由“基础镜像”和一组命令组成。这样说会有一些抽象难以理解,可以先看一个Dockerfile例子:

# 基础镜像为java:8
FROM java:8

# 将当前目录的文件拷贝到容器的/home/user/app目录
COPY . /home/user/app
# 设置当前目录为/home/user/app,等同于cd /home/user/app
WORKDIR /home/user/app
# 执行javac命令,编译拷贝过来的Hello.java
RUN javac Hello.java
# 执行java命令,运行Hello代码
CMD ["java", "Hello"]

对应代码:

public class Hello{
    public static void main(String[] args){
        System.out.println("hello world!!!");
    }
}

目录结构:

➜  my-docker ll
total 16
-rw-r--r--  1 yingong  staff   101B 12 23 23:21 Dockerfile
-rw-r--r--  1 yingong  staff   106B 12 23 22:31 Hello.java

Docker文档中是一个Python的例子,我这里换成了一个Java版本的,可以互相参考。有了Dockerfile,我们就可以通过Dockerfile构建出我们自己的镜像。

➜  my-docker docker build -t hello:0.0.1 .
Sending build context to Docker daemon  15.87kB
Step 1/5 : FROM java:8
 ---> d23bdf5b1b1b
Step 2/5 : COPY . /home/user/app
 ---> 11d307e7dfa5
Step 3/5 : WORKDIR /home/user/app
 ---> Running in bde0589938cd
Removing intermediate container bde0589938cd
 ---> 9f2c2cf0481f
Step 4/5 : RUN javac Hello.java
 ---> Running in 82d3866d5d1d
Removing intermediate container 82d3866d5d1d
 ---> c3c5c98c72a9
Step 5/5 : CMD ["java", "Hello"]
 ---> Running in 387930209ac6
Removing intermediate container 387930209ac6
 ---> a28a3c1f1802
Successfully built a28a3c1f1802
Successfully tagged hello:0.0.1

再次执行docker image ls,可以看到刚刚构建的hello:0.0.1镜像:

➜  my-docker docker image ls
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
hello                              0.0.1               a28a3c1f1802        3 minutes ago       643MB

使用构建的镜像创建一个容器:

➜  my-docker docker run hello:0.0.1
hello world!!!

Service

Service又是什么呢?在Docker文档中举了个例子。假设现在有一个视频分享网站,那么网站会有将应用数据存储到db的service,也会有将用户上传的视频文件在后台进行转码的service。我理解service是组成一个分布式系统的基础单位。

Docker为什么要搞出一个Service的概念?按上面的定义,假设有一个分布式系统包含登录、注册两个service,平时登录请求多,注册请求少。如何才能快速实现部署10个包含登录service的容器,2个包含注册service的容器?文档中给出的解决方案是docker-compose。

先看一下文档中给出的示例:

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

先记住几个关键属性:

  • image:指定使用哪个镜像
  • replicas:部署几个容器
  • cpus:分配的cpu资源
  • memory:分配的内存资源
  • ports:端口映射,将容器暴露的端口映射到宿主机上。4000是宿主机端口,80是容器端口。

这里我们还是使用一个springboot的镜像作为对比:

version: "3"
services:
  web:
    image: springio/gs-spring-boot-docker
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1"
          memory: 512M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:8080"
    networks:
      - webnet
networks:
  webnet:

差别有3点:

  • 镜像:替换为springio/gs-spring-boot-docker
  • memory:改成了512M,一开始忘记改了,沿用了文档中的50M,结果还没启动完内存耗尽容器就被kill了。
  • ports:springboot镜像的端口是8080

下面开始部署服务:

# 后面会解释这个命令的含义,目前先不用理解。
docker swarm init

# 通过docker-compose.yml部署一个名称为springboot的service
docker stack deploy -c docker-compose.yml springboot

查看部署的服务,可以看到服务产生了3个容器:

➜  ~ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                   PORTS
qkr25qd45e8s        springboot_web      replicated          3/3                 springio/gs-spring-boot-docker:latest   *:4000->8080/tcp

现在可以尝试访问一下镜像中的服务,映射到宿主机的端口是4000:

➜  ~ curl -4 http://localhost:4000
Hello Docker World%

Swarms

Swarms又是什么?想象一下公司的线上环境,假设我们公司有一个机房,机房里有30台物理机。现在我想部署10个包含登录服务的容器,怎么分配这些容器?Swarms负责的就是容器的管理、调度。

为了测试Swarms的,首先我们要创建2台虚拟机,模拟机房中的物理机器。

docker-machine create vm1 --virtualbox-boot2docker-url "https://github.com/boot2docker/boot2docker/releases/download/v18.06.1-ce/boot2docker.iso"

docker-machine create vm2 --virtualbox-boot2docker-url "https://github.com/boot2docker/boot2docker/releases/download/v18.06.1-ce/boot2docker.iso"

上面的命令创建了vm1和vm2两台虚拟机,通过docker-machine命令可以查看创建的虚拟机:

➜  ~ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
vm1    -        virtualbox   Running   tcp://192.168.99.100:2376           v18.06.1-ce
vm2    -        virtualbox   Running   tcp://192.168.99.101:2376           v18.06.1-ce

不要使用docker文档中的命令创建虚拟机,v18.09.0版本的boot2docker会存在端口映射问题。对应的issue

创建虚拟机后,需要让两台虚拟机加入Swarms集群,变成Swarms节点:

# 将vm1设置主节点 负责处理容器调度命令
docker-machine ssh vm1 "docker swarm init --advertise-addr 192.168.99.100"

# 让vm2加入Swarms,token在上面命令的输入中获取
docker-machine ssh vm2 "docker swarm join --token xxxx 192.168.99.100:2377"

做完上面的操作,我们可以先执行eval $(docker-machine env vm1)命令将当前会话切换的虚拟机vm1上。执行docker node ls命令应该可以看到:

➜  ~ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
x1tbu3a6mnasogkg6q80fk4eh *   vm1                 Ready               Active              Leader              18.06.1-ce
kwd6sb94neosed9ngalihltda     vm2                 Ready               Active                                  18.06.1-ce

现在Swarms已经准备好部署服务了,再次执行部署服务命令:

➜  ~ docker stack deploy -c docker-compose.yml hello
...

## 已部署的服务
➜  ~ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                   PORTS
yjtnmt2j5oxf        hello_web           replicated          3/3                 springio/gs-spring-boot-docker:latest   *:4000->8080/tcp

## 产生的容器
➜  ~ docker service ps yjtnmt2j5oxf
ID                  NAME                IMAGE                                   NODE                DESIRED STATE       CURRENT STATE         ERROR               PORTS
lfphgzdt1qml        hello_web.1         springio/gs-spring-boot-docker:latest   vm2                 Running             Running 8 hours ago
zjoji8nthzei        hello_web.2         springio/gs-spring-boot-docker:latest   vm1                 Running             Running 8 hours ago
t1qsyy8yjv1u        hello_web.3         springio/gs-spring-boot-docker:latest   vm2                 Running             Running 8 hours ago

可以看到3个容器,其中2个部署在了vm2上,1个部署在vm1上。

Swarms只是用来了解容器调度的概念,不需要深入学习。有兴趣的可以多了解下k8s。

Stack

终于到了最后一个概念:Stack。简单说一下,Stack和Service可以对比着看。Service中只有一个服务,Stack是服务的集合。A stack is a group of interrelated services that share dependencies, and can be orchestrated and scaled together。Stack是一组互相关联的服务,它们共享依赖,可以一起被编排和扩缩容。

本文介绍了如何构建自己的镜像、通过镜像启动容器,以及Service、Swarms、Stack分别是什么含义。更多内容会在后续《架构》专辑使用docker搭建环境时继续分析。欢迎关注个人公众号随时交流技术。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 一、Docker简介 1、Docker是什么? 是一个开源的容器引擎,基于go语言开发 2、特点 轻量级,可移植的...
    chuckielove阅读 1,578评论 0 2
  • 一.前言 最近玩了玩Docker, 从刚听说它到下载使用时间相隔了3年 - -, 因为最近刚刚学习完SpringC...
    objcat阅读 6,128评论 2 28
  • Docker入门 1. 简介 (1)容器(Container) vs 虚拟机(Virtual Machine) 容...
    四毛荷东阅读 605评论 0 1
  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,506评论 15 147
  • 最近在学习得到的财务课,里面一开始就说了这句话:这个世界上的所有事物,只要它在运行,本质上都不是隐藏起来的,而是在...
    吾三省s阅读 150评论 0 0