Docker的二次開發(fā):一個(gè)docker容器的守護(hù)程序

介紹

docker的sdk的官方介紹的樣例有g(shù)o和Python的如筛,并包含了如下對(duì)docker二次開發(fā)的幾種簡(jiǎn)單的實(shí)現(xiàn)

具體代碼請(qǐng)移步上述鏈接攒读。

這篇主要講講怎樣用go對(duì)docker進(jìn)行簡(jiǎn)單的二次開發(fā):一個(gè)docker容器的守護(hù)程序

kubernetes就是利用go對(duì)docker進(jìn)行二次開發(fā)以管理成千上萬(wàn)的docker容器的成功案例

kubernetes部署需要踩很多坑,但是有時(shí)我們只需要對(duì)docker進(jìn)行一次簡(jiǎn)單的二次開發(fā)以滿足業(yè)務(wù)的需求鹏秋,如新上線一個(gè)版本,我們需要在docker容器中部署燕雁,此時(shí)就可以對(duì)docker進(jìn)行二次開發(fā)以滿足我們的需求窒所。
需求一

  • 從git或svn上拉取最新的代碼楣嘁,并將其編譯成go的二進(jìn)制可運(yùn)行文件
  • 從docker倉(cāng)庫(kù)中拉需要的鏡像
  • 在鏡像的基礎(chǔ)上創(chuàng)建容器磅轻,包括配置容器的一些參數(shù)等等
  • 啟動(dòng)容器

這樣當(dāng)我們需要發(fā)布一個(gè)項(xiàng)目的新版本時(shí)直接運(yùn)行這個(gè)程序就能做到一鍵發(fā)布珍逸。一個(gè)容器運(yùn)行時(shí),就像一個(gè)操作系統(tǒng)運(yùn)行一樣聋溜,也有崩潰的時(shí)候谆膳,此時(shí)我們需要一個(gè)監(jiān)聽docker容器的健康狀況來(lái)以防一些意外
需求二

  • 監(jiān)聽docker容器運(yùn)行時(shí)的相關(guān)參數(shù)
  • 針對(duì)獲取到的參數(shù)做出相應(yīng)的處理,如mem使用打到80%時(shí)發(fā)送郵件通知小組的開發(fā)人員
  • 在docker容器崩潰時(shí)能重新啟動(dòng)該容器

假設(shè)需求

現(xiàn)在我就上面介紹的兩個(gè)需求簡(jiǎn)單綜合一下勤婚,以完成一個(gè)自己的需求

  1. 假設(shè)本地已有我們需要的docker image
  2. 檢查docker container中是否已存在目標(biāo)容器
  3. 若有摹量,則跳轉(zhuǎn)到第5步
  4. 若沒(méi)有,創(chuàng)建一個(gè)從container
  5. 啟動(dòng)該容器并按時(shí)檢查該container的狀態(tài)
  6. 若該container已崩潰馒胆,那么該程序能自動(dòng)重啟container

附:我們所期望的container內(nèi)部還掛在了一個(gè)宿主機(jī)的目錄

以上就是本篇文章將要實(shí)現(xiàn)的功能

正篇

SDK的安裝

go get github.com/docker/docker/client

安裝成功之后將$GOPATH/src/github.com/docker/docker下的vendor中的文件拷貝到$GOPATH/src下,然后刪除vendor文件

注:如果不進(jìn)行上述操作凝果,會(huì)有包沖突問(wèn)題祝迂,比如import包github.com/docker/go-connections/nat時(shí),程序優(yōu)先找到的是github.com/docker/docker/vendor/下的github.com/docker/go-connections/nat包器净,而不是$GOPATH/src/github.com/docker/go-connections/nat包型雳,所以會(huì)有包沖突

實(shí)現(xiàn)

注:最終目的是啟動(dòng)docker容器之后還要運(yùn)行其中的ginDocker服務(wù),本篇程序?qū)崿F(xiàn)的 部分功能 和如下的docker命令的效果一樣

docker run -it --name mygin-latest -p 7070:7070 -v /home/youngblood/Go/src/ginDocker:/go/src/ginDocker -w /go/src/ginDocker my-gin
1.檢查本地是否有我們需求的image

這里有很多方法可以實(shí)現(xiàn)這個(gè)山害,就像運(yùn)行docker pull命令時(shí)一樣纠俭,docker首先會(huì)檢查本地是否有該image,如果沒(méi)有才去docker hub 拉取這個(gè)image浪慌,所以這里我們直接使用代碼拉取鏡像即可冤荆,類似于這樣(但是該篇示例程序中并沒(méi)有寫拉取鏡像的代碼,因?yàn)樵撶R像是本地自己創(chuàng)建的一個(gè)鏡像权纤,和Docker中g(shù)o web項(xiàng)目部署中的鏡像是一樣的)

    rc, err := cli.ImagePull(ctx, "busybox", types.ImagePullOptions{})
    if err != nil {
        panic(err)
    }
    defer rc.Close()
2.檢查docker container中是否已存在目標(biāo)容器

當(dāng)創(chuàng)建一個(gè)container時(shí)钓简,顯示的給函數(shù)傳遞一個(gè)container name,那么之后我們?cè)俅芜\(yùn)行這個(gè)程序時(shí)同樣會(huì)創(chuàng)建同名的container汹想。但是外邓,docker中不允許存在同名的container,所以會(huì)創(chuàng)建失敗古掏,這樣就可以在創(chuàng)建container時(shí)確認(rèn)該container是否存在损话,代碼如下

    imageName := "my-gin:latest"
    cont, err := cli.ContainerCreate(ctx, &container.Config{
        Image:      imageName,               //Docker基于該鏡像創(chuàng)建容器
        Tty:        true,                    //docker run 命令的-t
        OpenStdin:  true,                    //docker run命令的-i
        Cmd:        []string{"./ginDocker2"},//docker容器中執(zhí)行的命令
        WorkingDir: "/go/src/ginDocker2",    //docker容器工作目錄
        ExposedPorts: nat.PortSet{            //docker容器對(duì)外開放的端口
            "7070": struct{}{},
        },
    }, &container.HostConfig{
        PortBindings: nat.PortMap{
            "7070": []nat.PortBinding{nat.PortBinding{//docker容器映射到宿主機(jī)的端口
                HostIP:   "0.0.0.0",
                HostPort: "7070",
            }},
        },
        Mounts: []mount.Mount{//docker容器卷掛載
            mount.Mount{
                Type:   mount.TypeBind,
                Source: "/home/youngblood/Go/src/ginDocker2",
                Target: "/go/src/ginDocker2",
            },
        },
    }, nil, "mygin-latest")

關(guān)于上述代碼作如下簡(jiǎn)述:

  1. &container.Config中的Tty和OpenStdin是-it標(biāo)識(shí),WorkingDir是-w標(biāo)識(shí)槽唾,ExposePorts是容器對(duì)外開放的端口丧枪。
  2. &container.HostConfig中PortMap表示端口映射,是-p標(biāo)識(shí)夏漱,注意這里必須和ExposedPorts配對(duì)使用豪诲,也就是說(shuō)容器開放了哪個(gè)端口,哪個(gè)端口才能映射到宿主機(jī)上挂绰,否則即使能映射成功屎篱,由于該端口容器未開放服赎,也不能訪問(wèn)服務(wù);Mounts是-v標(biāo)識(shí)交播,其中的Type有4種重虑,分別是TypeBind="bind",TypeVolume="volume"秦士,TypeTmpfs="tmpfs"缺厉,TypeNamedPipe="npipe",其中bind表示掛在到host dir隧土,所以這里選擇使用TypeBind提针。
  3. nil表示的是*net.NetWorkingConfig,由于此處沒(méi)有配置曹傀,所以使用nil
  4. "mygin-latest"表示容器的name
3. 啟動(dòng)該容器并按時(shí)檢查該container的狀態(tài)

啟動(dòng)容器

        //啟動(dòng)容器
        if err = cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil {
            panic(err)
        }

獲取容器內(nèi)部的運(yùn)行狀態(tài)

    status, err = cli.ContainerStats(ctx, id, true)
    if err != nil {
        panic(err)
    }
    io.Copy(os.Stdout, status.Body)

將status.Body輸出到標(biāo)準(zhǔn)輸出中辐脖,你會(huì)看到控制臺(tái)不斷的輸出容器的狀態(tài)參數(shù)等,你可以根據(jù)status.Body獲取你關(guān)心的一些參數(shù)

4.若該container已崩潰皆愉,那么該程序能自動(dòng)重啟container

下列代碼能獲取到正在運(yùn)行的container嗜价。利用container的name屬性來(lái)判斷該container是否是在運(yùn)行。

        //獲取正在運(yùn)行的container list
        containerList, err := cli.ContainerList(ctx, types.ContainerListOptions{})
        if err != nil {
            panic(err)
        }

        var contTemp types.Container
        //找出名為“mygin-latest”的container并將其存入contTemp中
        for _, v1 := range containerList {
            log.Println("name=", v1.ID)
            for _, v2 := range v1.Names {
                if v2 == "/mygin-latest" {
                    contTemp = v1//若contTemp為空幕庐,則該容器未運(yùn)行久锥;反之,正在運(yùn)行
                    break
                }
            }
        }

綜合

目前异剥,每一步最基本的做法我們已經(jīng)實(shí)現(xiàn)并貼出了代碼瑟由,接下來(lái)的工作就是將這個(gè)工作整合到一起,做一個(gè)簡(jiǎn)單的封裝并做好流程調(diào)度即可届吁。

ginDocker2

是我們要在docker容器中發(fā)布的一個(gè)Go項(xiàng)目
代碼如下

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    router.GET("/hello/:name", func(c *gin.Context) {
        name := c.Param("name")
        c.String(http.StatusOK, "hello %s", name)
    })
    router.Run(":7070")
}

注:由于在下面的這個(gè)程序中創(chuàng)建容器時(shí)沒(méi)辦法一次執(zhí)行多個(gè)cmd命令错妖,所以這里的ginDocker2是先在外面的終端執(zhí)行g(shù)o build ginDocker2,在目錄ginDocker2下生成一個(gè)可執(zhí)行的二進(jìn)制文件ginDocker2

守護(hù)程序containerDeamon

聲明

  • 該程序相當(dāng)于執(zhí)行命令:docker run -it --name mygin-latest -p 7070:7070 -v /home/youngblood/Go/src/ginDocker:/go/src/ginDocker -w /go/src/ginDocker my-gin
  • 該程序會(huì)檢測(cè)名為mygin-latest的容器是否存在疚沐,并檢查該容器是否在運(yùn)行暂氯,若沒(méi)有,則啟動(dòng)容器并運(yùn)行其中的程序

代碼

package main

import (
    "io"
    "log"
    "os"
    "time"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/api/types/mount"
    "github.com/docker/docker/client"
    "github.com/docker/go-connections/nat"
    "golang.org/x/net/context"
)

const (
    imageName     string   = "my-gin:latest"                      //鏡像名稱
    containerName string   = "mygin-latest"                       //容器名稱
    indexName     string   = "/" + containerName                  //容器索引名稱亮蛔,用于檢查該容器是否存在是使用
    cmd           string   = "./ginDocker2"                       //運(yùn)行的cmd命令痴施,用于啟動(dòng)container中的程序
    workDir       string   = "/go/src/ginDocker2"                 //container工作目錄
    openPort      nat.Port = "7070"                               //container開放端口
    hostPort      string   = "7070"                               //container映射到宿主機(jī)的端口
    containerDir  string   = "/go/src/ginDocker2"                 //容器掛在目錄
    hostDir       string   = "/home/youngblood/Go/src/ginDocker2" //容器掛在到宿主機(jī)的目錄
    n             int      = 5                                    //每5s檢查一個(gè)容器是否在運(yùn)行

)

func main() {
    ctx := context.Background()
    cli, err := client.NewEnvClient()
    defer cli.Close()
    if err != nil {
        panic(err)
    }
    checkAndStartContainer(ctx, cli)
}

//創(chuàng)建容器
func createContainer(ctx context.Context, cli *client.Client) {
    //創(chuàng)建容器
    cont, err := cli.ContainerCreate(ctx, &container.Config{
        Image:      imageName,     //鏡像名稱
        Tty:        true,          //docker run命令中的-t選項(xiàng)
        OpenStdin:  true,          //docker run命令中的-i選項(xiàng)
        Cmd:        []string{cmd}, //docker 容器中執(zhí)行的命令
        WorkingDir: workDir,       //docker容器中的工作目錄
        ExposedPorts: nat.PortSet{
            openPort: struct{}{}, //docker容器對(duì)外開放的端口
        },
    }, &container.HostConfig{
        PortBindings: nat.PortMap{
            openPort: []nat.PortBinding{nat.PortBinding{
                HostIP:   "0.0.0.0", //docker容器映射的宿主機(jī)的ip
                HostPort: hostPort,  //docker 容器映射到宿主機(jī)的端口
            }},
        },
        Mounts: []mount.Mount{ //docker 容器目錄掛在到宿主機(jī)目錄
            mount.Mount{
                Type:   mount.TypeBind,
                Source: hostDir,
                Target: containerDir,
            },
        },
    }, nil, containerName)
    if err == nil {
        log.Printf("success create container:%s\n", cont.ID)
    } else {
        log.Println("failed to create container!!!!!!!!!!!!!")
    }
}

//啟動(dòng)容器
func startContainer(ctx context.Context, containerID string, cli *client.Client) error {
    err := cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{})
    if err == nil {
        log.Printf("success start container:%s\n", containerID)
    } else {
        log.Printf("failed to start container:%s!!!!!!!!!!!!!\n", containerID)
    }
    return err
}

//將容器的標(biāo)準(zhǔn)輸出輸出到控制臺(tái)中
func printConsole(ctx context.Context, cli *client.Client, id string) {
    //將容器的標(biāo)準(zhǔn)輸出顯示出來(lái)
    out, err := cli.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true})
    if err != nil {
        panic(err)
    }
    io.Copy(os.Stdout, out)

    //容器內(nèi)部的運(yùn)行狀態(tài)
    status, err := cli.ContainerStats(ctx, id, true)
    if err != nil {
        panic(err)
    }
    io.Copy(os.Stdout, status.Body)
}

//檢查容器是否存在并啟動(dòng)容器
func checkAndStartContainer(ctx context.Context, cli *client.Client) {
    for {
        select {
        case <-isRuning(ctx, cli):
            //該container沒(méi)有在運(yùn)行
            //獲取所有的container查看該container是否存在
            contTemp := getContainer(ctx, cli, true)
            if contTemp.ID == "" {
                //該容器不存在,創(chuàng)建該容器
                log.Printf("the container name[%s] is not exists!!!!!!!!!!!!!\n", containerName)
                createContainer(ctx, cli)
            } else {
                //該容器存在究流,啟動(dòng)該容器
                log.Printf("the container name[%s] is exists\n", containerName)
                startContainer(ctx, contTemp.ID, cli)
            }

        }
    }
}

//獲取container
func getContainer(ctx context.Context, cli *client.Client, all bool) types.Container {
    containerList, err := cli.ContainerList(ctx, types.ContainerListOptions{All: all})
    if err != nil {
        panic(err)
    }
    var contTemp types.Container
    //找出名為“mygin-latest”的container并將其存入contTemp中
    for _, v1 := range containerList {
        for _, v2 := range v1.Names {
            if v2 == indexName {
                contTemp = v1
                break
            }
        }
    }
    return contTemp
}

//容器是否正在運(yùn)行
func isRuning(ctx context.Context, cli *client.Client) <-chan bool {
    isRun := make(chan bool)
    var timer *time.Ticker
    go func(ctx context.Context, cli *client.Client) {
        for {
            //每n s檢查一次容器是否運(yùn)行

            timer = time.NewTicker(time.Duration(n) * time.Second)
            select {
            case <-timer.C:
                //獲取正在運(yùn)行的container list
                log.Printf("%s is checking the container[%s]is Runing??", os.Args[0], containerName)
                contTemp := getContainer(ctx, cli, false)
                if contTemp.ID == "" {
                    log.Print(":NO")
                    //說(shuō)明container沒(méi)有運(yùn)行
                    isRun <- true
                } else {
                    log.Print(":YES")
                    //說(shuō)明該container正在運(yùn)行
                    go printConsole(ctx, cli, contTemp.ID)
                }
            }

        }
    }(ctx, cli)
    return isRun
}

說(shuō)明:

  • const中的變量按自己的需求定制
  • 該程序名稱叫做containerDeamon辣吃,運(yùn)行時(shí)前面加上sudo,否則會(huì)提示權(quán)限不夠
  • 運(yùn)行成功之后控制臺(tái)會(huì)打印很多日志芬探,此時(shí)可以注釋掉isRuning函數(shù)中的 go printConsole()函數(shù)重新編譯運(yùn)行神得,此時(shí)的日志更方便于閱讀

現(xiàn)在沒(méi)有名為mygin-latest的docker容器,啟動(dòng)containerDeamon守護(hù)進(jìn)程之后看看控制臺(tái)打印了什么

2017/11/18 00:41:16 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:16 :NO
2017/11/18 00:41:16 the container name[mygin-latest] is not exists!!!!!!!!!!!!!
2017/11/18 00:41:16 success create container:e1d91ca1cc2adf84675c9bb90854e2ce709617088a6f7090127090ad4230fcf8
2017/11/18 00:41:21 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:21 :NO
2017/11/18 00:41:21 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:21 :NO
2017/11/18 00:41:21 the container name[mygin-latest] is exists
2017/11/18 00:41:22 success start container:e1d91ca1cc2adf84675c9bb90854e2ce709617088a6f7090127090ad4230fcf8
2017/11/18 00:41:26 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:26 :YES
2017/11/18 00:41:27 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:27 :YES

此時(shí)用命令sudo docker ps查看正在運(yùn)行的docker容器

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
e1d91ca1cc2a        my-gin:latest       "./ginDocker2"      18 seconds ago      Up 12 seconds       0.0.0.0:7070->7070/tcp   mygin-latest

用docker stop e1d91ca1cc2a停掉該容器之后會(huì)在containerDeamon的控制臺(tái)看到如下輸出

2017/11/18 00:41:41 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:41 :YES
2017/11/18 00:41:42 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:42 :NO
2017/11/18 00:41:42 the container name[mygin-latest] is exists
2017/11/18 00:41:43 success start container:e1d91ca1cc2adf84675c9bb90854e2ce709617088a6f7090127090ad4230fcf8
2017/11/18 00:41:46 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:46 :YES
2017/11/18 00:41:47 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:47 :YES
2017/11/18 00:41:48 ./containerDeamon is checking the container[mygin-latest]is Runing??
2017/11/18 00:41:48 :YES

此時(shí)再用命令sudo docker ps查看正在運(yùn)行的docker容器偷仿,發(fā)現(xiàn)該容器已被啟動(dòng)哩簿。

最后宵蕉,讓我們?cè)跒g覽器訪問(wèn)localhost:7070/hello/初級(jí)賽亞人看看容器的程序啟用的端口是否成功映射到了宿主機(jī)的7070端口
截圖

hello初級(jí)賽亞人.png

至此,一個(gè)docker container的守護(hù)程序就完成了节榜,如果你對(duì)上面的代碼有任何疑問(wèn)歡迎提問(wèn)羡玛,覺得不好的地方請(qǐng)斧正。
關(guān)注喜歡隨便點(diǎn)宗苍,看看也行——支持是對(duì)我的最大鼓勵(lì)(初級(jí)賽亞人)稼稿。
——待更——
最近對(duì)這個(gè)守護(hù)程序,忽然發(fā)現(xiàn)有一種情況被忽略了讳窟,就是在檢查容器是否在運(yùn)行時(shí)让歼,如果容器已在運(yùn)行,而容器中的go程序并沒(méi)有在運(yùn)行挪钓,所以會(huì)導(dǎo)致雖然容器在運(yùn)行是越,但是我們的服務(wù)并沒(méi)有發(fā)發(fā)布,之后會(huì)抽個(gè)時(shí)間將這種情況補(bǔ)上碌上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浦徊,隨后出現(xiàn)的幾起案子馏予,更是在濱河造成了極大的恐慌,老刑警劉巖盔性,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霞丧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冕香,警方通過(guò)查閱死者的電腦和手機(jī)蛹尝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悉尾,“玉大人突那,你說(shuō)我怎么就攤上這事」姑校” “怎么了愕难?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)惫霸。 經(jīng)常有香客問(wèn)我猫缭,道長(zhǎng),這世上最難降的妖魔是什么壹店? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任猜丹,我火速辦了婚禮,結(jié)果婚禮上硅卢,老公的妹妹穿的比我還像新娘射窒。我一直安慰自己藏杖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布轮洋。 她就那樣靜靜地躺著制市,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弊予。 梳的紋絲不亂的頭發(fā)上祥楣,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音汉柒,去河邊找鬼误褪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碾褂,可吹牛的內(nèi)容都是我干的兽间。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼正塌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘀略!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起乓诽,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤帜羊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鸠天,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼育,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年稠集,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奶段。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剥纷,死狀恐怖痹籍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筷畦,我是刑警寧澤词裤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鳖宾,受9級(jí)特大地震影響吼砂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鼎文,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一渔肩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拇惋,春花似錦周偎、人聲如沸抹剩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澳眷。三九已至,卻和暖如春蛉艾,著一層夾襖步出監(jiān)牢的瞬間钳踊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工勿侯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拓瞪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓助琐,卻偏偏與公主長(zhǎng)得像祭埂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兵钮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • Docker — 云時(shí)代的程序分發(fā)方式 要說(shuō)最近一年云計(jì)算業(yè)界有什么大事件蛆橡?Google Compute Engi...
    ahohoho閱讀 15,508評(píng)論 15 147
  • 一、Docker 簡(jiǎn)介 Docker 兩個(gè)主要部件:Docker: 開源的容器虛擬化平臺(tái)Docker Hub: 用...
    R_X閱讀 4,379評(píng)論 0 27
  • 轉(zhuǎn)載自 http://blog.opskumu.com/docker.html 一掘譬、Docker 簡(jiǎn)介 Docke...
    極客圈閱讀 10,475評(píng)論 0 120
  • 文/一位喵先生 有人說(shuō)屁药,愛情里最好的狀態(tài)剛開始的曖昧?xí)r光,當(dāng)你收到他的信息時(shí)柏锄,嘴角不自覺地上揚(yáng)酿箭,給他回復(fù)的每字每句...
    一位喵先生閱讀 958評(píng)論 0 5
  • 我喜歡著六年級(jí)的那個(gè)男孩 他不喜歡我 我放棄了他 開始努力學(xué)習(xí) 開始向往初中的生活 初一 遇見了他 第一眼看見...
    放開他閱讀 160評(píng)論 0 1