ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AWD平台3 狠狠记录,从镜像打包到ssh自启动再到指定容器中root密码

2022-07-15 19:32:07  阅读:153  来源: 互联网

标签:stream app u003e --- ssh easyweb 自启动 AWD


狠狠记录,从镜像打包到ssh自启动再到指定容器中root密码。

镜像打包

其实镜像打包是一件非常简单的事情,但是用代码实现的时候还是会有坑。

先说明,我用的是docker官方提供的sdk

这里先展示我的dockerfile

FROM alpine:3.14
RUN mkdir "/app"
WORKDIR "/app"
COPY easyweb /app/app
COPY start.sh /app/start.sh
RUN apk 'add' '--no-cache' 'openssh-server'
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN ssh-keygen -A 
CMD ./start.sh

这个dockerfile会拷一些东西(编译好的go程序和启动脚本)进容器,然后启动一个脚本,这个脚本做两件事:启动ssh,启动我的web应用。

打包必须用tar包。

打包函数长这个样子

func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)

他的第二个参数buildContext是一个io.Reader类型的,这个上下文指的就是我们打包镜像时的上下文,而且必须是一个文件,不能是文件夹,因此我们需要达成一个tar包

所以写出了这样的代码

func buildImg() {
	ctx := context.Background()
	cli, _ := client.NewClientWithOpts()

	buildContext, _ := os.Open("../eastweb.tar")
	defer buildContext.Close()
	buildResponse, err := cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{
		Dockerfile:     "Dockerfile",    //相对buildContext下的路径
		SuppressOutput: false,
		Remove:         true,
		ForceRemove:    true,
		Tags:           []string{"web"},   // 给你的镜像打的tag
	})
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	response, err := ioutil.ReadAll(buildResponse.Body)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(string(response))
}

这里面就注意Dockerfile这一项,它指定的是Dockerfile的相对路径而不是绝对路径。

这里展示我的tar包是啥样的:

image-20220715164252836

所以这里就直接填"Dockerfile".

imageBuild会返回一个buildResponse,它是这么定义的

// ImageBuildResponse holds information
// returned by a server after building
// an image.
type ImageBuildResponse struct {
	Body   io.ReadCloser
	OSType string
}

我们于是就用了ioutil.ReadAll去读取出来,其实它里面的内容就是打包镜像的过程,例如下面这个:

ymk@ymk:test$ ./test 
{"stream":"Step 1/9 : FROM alpine:3.14"}
{"stream":"\n"}
{"stream":" ---\u003e 0a97eee8041e\n"}
{"stream":"Step 2/9 : RUN mkdir \"/app\""}
{"stream":"\n"}
{"stream":" ---\u003e Using cache\n"}
{"stream":" ---\u003e 3ebb98b41468\n"}
{"stream":"Step 3/9 : WORKDIR \"/app\""}
{"stream":"\n"}
{"stream":" ---\u003e Using cache\n"}
{"stream":" ---\u003e 103148b20052\n"}
{"stream":"Step 4/9 : COPY easyweb /app/app"}
{"stream":"\n"}
{"stream":" ---\u003e Using cache\n"}
{"stream":" ---\u003e 7071bec37683\n"}
{"stream":"Step 5/9 : COPY start.sh /app/start.sh"}
{"stream":"\n"}
{"stream":" ---\u003e 4fa1d332a3e9\n"}
{"stream":"Step 6/9 : RUN apk 'add' '--no-cache' 'openssh-server'"}
{"stream":"\n"}
{"stream":" ---\u003e Running in 9a47f0e0a94d\n"}
{"stream":"fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz\n"}
{"stream":"fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz\n"}
{"stream":"(1/3) Installing openssh-keygen (8.6_p1-r3)\n"}
{"stream":"(2/3) Installing openssh-server-common (8.6_p1-r3)\n"}
{"stream":"(3/3) Installing openssh-server (8.6_p1-r3)\n"}
{"stream":"Executing busybox-1.33.1-r6.trigger\n"}
{"stream":"OK: 7 MiB in 17 packages\n"}
{"stream":"Removing intermediate container 9a47f0e0a94d\n"}
{"stream":" ---\u003e 5fde29deeffc\n"}
{"stream":"Step 7/9 : RUN echo \"PermitRootLogin yes\" \u003e\u003e /etc/ssh/sshd_config"}
{"stream":"\n"}
{"stream":" ---\u003e Running in d2c2dca56a47\n"}
{"stream":"Removing intermediate container d2c2dca56a47\n"}
{"stream":" ---\u003e f6c202958b3a\n"}
{"stream":"Step 8/9 : RUN ssh-keygen -A"}
{"stream":"\n"}
{"stream":" ---\u003e Running in 1e611532d66f\n"}
{"stream":"ssh-keygen: generating new host keys: RSA "}
{"stream":"DSA "}
{"stream":"ECDSA "}
{"stream":"ED25519 "}
{"stream":"\n"}
{"stream":"Removing intermediate container 1e611532d66f\n"}
{"stream":" ---\u003e 0293f3603bc4\n"}
{"stream":"Step 9/9 : CMD ./start.sh"}
{"stream":"\n"}
{"stream":" ---\u003e Running in cedc3c842033\n"}
{"stream":"Removing intermediate container cedc3c842033\n"}
{"stream":" ---\u003e a6f4ffdd5139\n"}
{"aux":{"ID":"sha256:a6f4ffdd51398aa82411bf038a34d4a70c27a44aab78e14b2c5eeef55630b8c2"}}
{"stream":"Successfully built a6f4ffdd5139\n"}
{"stream":"Successfully tagged web:latest\n"}

注意Dockerfie中的相对路径

我们正常写Dockerfile,里面会涉及拷贝之类的操作,比如这一行

COPY easyweb /app/app

显然它就是把下图中的easyweb这个文件拷到了容器中的/app/app的位置,然后把工作目录设未/app,进去之后直接./app就能启动应用了,然而我用代码打包的镜像并不是这样的,我启动不了。

image-20220715183040106

原因是什么呢,这几个文件都在一个叫easyweb的文件夹里

image-20220715183248092

然后我对easyweb打包,达成easyweb.tar。

这时候我们点进easyweb.tar可以看到里面还有一层easyweb

image-20220715183407062

就是这一层导致了我的一个问题

在来看之前打包镜像的代码

buildContext, _ := os.Open("../easyweb.tar")
	defer buildContext.Close()
	buildResponse, err := cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{
		Dockerfile:     "Dockerfile",    //相对buildContext下的路径
		SuppressOutput: false,
		Remove:         true,
		ForceRemove:    true,
		Tags:           []string{"web"},   // 给你的镜像打的tag
	})

我的上下文是进入这个easyweb.tar这个tar包里面的一层,就是上面图里的easyweb文件夹那一层,然后这个上下文,就是Dockerfile相对路径所相对的上下文,所以这时候,Dockerfile里的相对路径不再相对自己,而是相对easyweb这个文件夹。于是COPY easyweb /app/app 变成了将easyweb这个文文件夹拷到/app目录下,并且改名为app,于是再使用./app启动就会报错。

如何解决呢?打包的时候不要夹上那一层,直接针对文件打包,这是我的另一个tar包

image-20220715183807896

打成这样就ok了。

或者改启动脚本。

alpine如何设置ssh自启动

其实,设置alpine就是把dockerfile写好就行了,但是我卡了好长时间,因为我分不清RUN,CMD,和ENTRYPOINT.

现在我分清了。

RUN

RUN后面执行的命令,的结果会体现在打好的镜像中,比如创建文件夹。但是你用RUN去启动程序不可以。

CMD

CMD可以有多条,但是只有最后一条有用,可以用来启动程序

ENTRYPOINT

ENTRYPOINT只有一条游泳,也是用来启动程序

重点是CMD和ENTRYPOINT最好只写一条

因为如果两个配合用,CMD的内容会加到ENTRYPOINT后面,具体看这张图

所以我这个镜像既要启动ssh,又要启动web进程,本来想用CMD的shell模式,通过

CMD  ./app && /usr/sbin/sshd

启动,但是我写反了,先启动./app,它是在前台启动的,因此后买你的ssh启动不了,我当时懵逼了,就换了方法,写了一个脚本用ENTRYPOINT启动脚本,但是用脚本也要考虑前后台的问题。

然后回过头我们浅说一下怎么给alpine搞ssh自启动吧

其实就是这几行

RUN apk 'add' '--no-cache' 'openssh-server'
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN ssh-keygen -A 

把ssh的包下好,然后把"PermitRootLogin yes" 输入到配置文件中去,这样的话就可以使用root账号远程登录。

然后执行ssh-keygen -A 生成密钥啥的,就可以顺利启动了。

最后就是在容器启动时执行

/user/sbin/sshd

就ok了。

设置修改容器中root密码

现在ssh服务起起来了,如何给他指定我们想要的root密码呢?

我最初希望在容器启动的时候指定进去,但是只能通过CMD,但是Dickerfile有这类命令了,不兼容了,于是我想到一行命令就可以改密码了,就是这个

echo root:newpassword | chpasswd

但是我尝试用docker exec 容器名 echo root:newpassword | chpasswd 执行的,执行出错,说我认证令牌操作错误。然后只能用docker exec 容器名 passwd,但是这个需要两步去确认,考虑到我不太会用go语言搞命令行的操作,于是我去翻文档找到了三个函数

  • ContainerExecCreate
  • ContainerExecAttach
  • ContainerExecStart

这三个函数可以帮我完成。

代码如下

func exec() {
	ctx := context.Background()
	cli, err := client.NewClientWithOpts()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	id,_:= cli.ContainerExecCreate(ctx, "testweb", types.ExecConfig{
		AttachStderr: true,
		AttachStdin:  true,
		AttachStdout: true,
		Cmd:          []string{"passwd"},
	})

	res, _ := cli.ContainerExecAttach(ctx, id.ID, types.ExecStartCheck{})
	cli.ContainerExecStart(ctx, id.ID, types.ExecStartCheck{
		Tty: true,
	})

	res.Conn.Write([]byte("ymk0910!\n"))
	res.Conn.Write([]byte("ymk0910!\n"))
	fmt.Println("结束")
}

这个测试就是帮我把名为testweb的容器的root密码改成ymk0910!

他是这个原理,先把exec给Create出来,然后用ContainerExecAttach去跟踪这一条exec,然后Start执行,执行起来我们通过Attach给我们返回的Conn去代替命令行里写的过程,因为我们知道passwd他要先输密码再确认,所以我这里Write两边,然后就成功,我通过ssh root@ip 连上了容器。

当ssh能连上之后就好办了,可以通过ssh在容器里执行命令或者还通过这个方式,然后可以刷新flag,可以检测容器是否正常等等。

标签:stream,app,u003e,---,ssh,easyweb,自启动,AWD
来源: https://www.cnblogs.com/yumingkuan/p/16482597.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有