Skip to content

Commit

Permalink
run multiple containers
Browse files Browse the repository at this point in the history
  • Loading branch information
xwjahahahaha committed Nov 14, 2021
1 parent 0babf73 commit c89c49d
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 55 deletions.
2 changes: 1 addition & 1 deletion cgroups/subsystems/utils.go
Expand Up @@ -61,7 +61,7 @@ func GetCgroupPath(subsystem string, cPath string, autoCreate bool) (string, err
if _, err := os.Stat(absolutePath); err == nil || (autoCreate && os.IsNotExist(err)) {
if os.IsNotExist(err) {
// 创建文件夹
if err := os.Mkdir(absolutePath, 0755); err != nil {
if err := os.MkdirAll(absolutePath, 0755); err != nil {
return "", fmt.Errorf("error create cgroup dir %v", err)
}
return absolutePath, nil
Expand Down
20 changes: 17 additions & 3 deletions cmd/commands.go
Expand Up @@ -16,10 +16,11 @@ const (
var (
tty bool // 是否交互式执行
ResourceLimitCfg = &subsystems.ResourceConfig{} // 资源限制配置
CgroupName = "myDockerTestCgroup" // 新建的cgroup的名称
CgroupName = "myDocker" // 新建的cgroup的名称
Volume string // 数据卷
Detach bool // 后台运行
Name string // 容器名称
Name string // 容器名称
ImageTarPath string // 镜像的tar包路径
)

var initDocker = &cobra.Command{
Expand All @@ -43,8 +44,11 @@ var runDocker = &cobra.Command{
// 两个标志不运行同时设置
return fmt.Errorf(" tty and detach can't both provided.")
}
// 生成容器ID
// 首先生成容器ID
id := container.RandStringContainerID(10)
// 获取交互flag值与command, 启动容器
container.Run(tty, strings.Split(args[0], " "), ResourceLimitCfg, CgroupName, Volume, Name)
container.Run(tty, strings.Split(args[0], " "), ResourceLimitCfg, CgroupName, Volume, Name, ImageTarPath, id)
return nil
},
}
Expand All @@ -58,3 +62,13 @@ var commitCommand = &cobra.Command{
container.CommitContainer(args[0])
},
}

var listContainers = &cobra.Command{
Use: "ps",
Short: "list all the containers",
Long: "list all the containers",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
container.ListAllContainers()
},
}
3 changes: 2 additions & 1 deletion cmd/init.go
@@ -1,7 +1,7 @@
package cmd

func init() {
rootCmd.AddCommand(initDocker, runDocker, commitCommand)
rootCmd.AddCommand(initDocker, runDocker, commitCommand, listContainers)
runDocker.Flags().BoolVarP(&tty, "tty", "t", false, "enable tty")
runDocker.Flags().StringVarP(&ResourceLimitCfg.MemoryLimit, "memory-limit", "m", "200m", "memory limit")
runDocker.Flags().StringVarP(&ResourceLimitCfg.CpuShare, "cpu-shares", "", "1024", "cpu shares")
Expand All @@ -10,4 +10,5 @@ func init() {
runDocker.Flags().StringVarP(&Volume, "volume", "v", "", "add a volume")
runDocker.Flags().BoolVarP(&Detach, "detach", "d", false, "Run container in background and print container ID")
runDocker.Flags().StringVarP(&Name, "container-name", "n", "", "set a container nickname")
runDocker.Flags().StringVarP(&ImageTarPath, "image-tar-path", "i", "./busybox.tar", "used image tar file path")
}
12 changes: 6 additions & 6 deletions container/process.go
Expand Up @@ -3,6 +3,7 @@ package container
import (
"os"
"os/exec"
"path/filepath"
"syscall"
"xwj/mydocker/log"
)
Expand All @@ -12,7 +13,7 @@ import (
// @param tty
// @return *exec.Cmd
// @return *os.File 管道写入端
func NewParentProcess(tty bool, volume string) (*exec.Cmd, *os.File) {
func NewParentProcess(tty bool, volume, ImageTarPath, cId string) (*exec.Cmd, *os.File) {
// 创建匿名管道
readPipe, writePipe, err := NewPipe()
if err != nil {
Expand All @@ -34,11 +35,10 @@ func NewParentProcess(tty bool, volume string) (*exec.Cmd, *os.File) {
cmd.Stderr = os.Stderr
}
// 创建新的工作空间
rootUrl := "./"
mntUrl := "./mnt"
imageName := "busybox"
NewWorkSpace(rootUrl, imageName, mntUrl, volume)
cmd.Dir = mntUrl // 设置进程启动的路径
rootUrl := "/var/lib/mydocker/aufs/" // 根目录
mntUrl := filepath.Join(rootUrl, "mnt", cId) // 容器运行空间
NewWorkSpace(rootUrl, ImageTarPath, mntUrl, volume, cId)
cmd.Dir = mntUrl // 设置进程启动的路径
// 在这里传入管道文件读取端的句柄
// ExtraFiles指定要由新进程继承的其他打开文件。它不包括标准输入、标准输出或标准错误。
cmd.ExtraFiles = []*os.File{readPipe}
Expand Down
85 changes: 80 additions & 5 deletions container/record.go
Expand Up @@ -4,10 +4,12 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"text/tabwriter"
"time"
"xwj/mydocker/log"
)
Expand All @@ -33,11 +35,11 @@ var (
ConfigName = "containerInfo.json"
)

// randStringContainerID
// RandStringContainerID
// @Description: 容器ID随机生成器
// @param n
// @return string
func randStringContainerID(n int) string {
func RandStringContainerID(n int) string {
if n < 0 || n > 32 {
n = 32
}
Expand All @@ -46,9 +48,14 @@ func randStringContainerID(n int) string {
return fmt.Sprintf("%x", hashBytes[:n])
}

func recordContainerInfo(cPID int, commandArray []string, cName string) (string, error) {
// 首先生成容器ID
id := randStringContainerID(IDLen)
// recordContainerInfo
// @Description: 记录一个容器的信息
// @param cPID
// @param commandArray
// @param cName
// @return string
// @return error
func recordContainerInfo(id string, cPID int, commandArray []string, cName string) (string, error) {
// 以当前时间为容器的创建时间
createTime := time.Now().Format("2006-01-02 15:04:05")
// 如果用户没有指定容器名就用容器ID做为容器名
Expand Down Expand Up @@ -91,9 +98,77 @@ func recordContainerInfo(cPID int, commandArray []string, cName string) (string,
return id, nil
}

// DeleteContainerInfo
// @Description: 删除一个容器的容器ID
// @param containerID
func DeleteContainerInfo(containerID string) {
dirUrl := filepath.Join(DefaultInfoLocation, containerID)
if err := os.RemoveAll(dirUrl); err != nil {
log.LogErrorFrom("DeleteContainerInfo", "RemoveAll", err)
}
}

// ListAllContainers
// @Description: 列出所有容器信息,输出到标准输出
func ListAllContainers() {
dirUrl := filepath.Join(DefaultInfoLocation)
// 读取该路径下的所有文件
files, err := ioutil.ReadDir(dirUrl)
if err != nil {
log.LogErrorFrom("ListAllContainers", "ReadDir", err)
return
}
var containers []*ContainerInfo
for _, file := range files {
tmpContainerInfo, err := getContainerInfo(file)
if err != nil {
log.LogErrorFrom("ListAllContainers", "getContainerInfo", err)
continue
}
containers = append(containers, tmpContainerInfo)
}
// 输出
// 使用tabwriter.NewWriter在控制台打印对齐的表格
w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
// 控制台输出的信息列
fmt.Fprint(w, "ID\tNAME\tPID\tSTATUS\tCOMMAND\tCREATED\n")
for _, item := range containers {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n",
item.Id,
item.Name,
item.Pid,
item.Status,
item.Command,
item.CreatedTime,
)
}
// 刷新标准输出刘缓冲区,将容器列表打印出来
if err := w.Flush(); err != nil {
log.LogErrorFrom("ListAllContainers", "Flush", err)
return
}
}

// getContainerInfo
// @Description: 获取一个容器的信息
// @param file
// @return *ContainerInfo
// @return error
func getContainerInfo(file os.FileInfo) (*ContainerInfo, error) {
// 获取文件名称
fileName := file.Name()
// 生成文件的绝对路径
filePath := filepath.Join(DefaultInfoLocation, fileName, ConfigName)
// 读取文件信息
content, err := ioutil.ReadFile(filePath)
if err != nil {
log.LogErrorFrom("getContainerInfo", "ReadFile", err)
return nil, err
}
var containerInfo ContainerInfo
if err := json.Unmarshal(content, &containerInfo); err != nil {
log.LogErrorFrom("getContainerInfo", "Unmarshal", err)
return nil, err
}
return &containerInfo, nil
}
32 changes: 21 additions & 11 deletions container/run.go
Expand Up @@ -3,23 +3,33 @@ package container
import (
"fmt"
"os"
"path/filepath"
"strings"
"xwj/mydocker/cgroups"
"xwj/mydocker/cgroups/subsystems"
"xwj/mydocker/log"
)

// Run
// @Description: 执行命令
// @Description: 运行
// @param tty
// @param cmdArray
// @param res
// @param cgroupName
// @param volume
// @param cName
// @param ImageTarPath
// @param cId
// @Description:
// @param tty
// @param cmdArray
// @param res
// @param cgroupName
// @param volume
// @param name
func Run(tty bool, cmdArray []string, res *subsystems.ResourceConfig, cgroupName string, volume, name string){
func Run(tty bool, cmdArray []string, res *subsystems.ResourceConfig, cgroupName string, volume, cName, ImageTarPath, cId string){
// 获取到管道写端
parent, pipeWriter := NewParentProcess(tty, volume)
parent, pipeWriter := NewParentProcess(tty, volume, ImageTarPath, cId)
if parent == nil {
log.LogErrorFrom("Run", "NewParentProcess", fmt.Errorf(" parent process is nil"))
return
Expand All @@ -31,30 +41,30 @@ func Run(tty bool, cmdArray []string, res *subsystems.ResourceConfig, cgroupName
log.Log.Error(err)
}
// 记录容器信息
containerInfo, err := recordContainerInfo(parent.Process.Pid, cmdArray, name)
containerInfo, err := recordContainerInfo(cId, parent.Process.Pid, cmdArray, cName)
if err != nil {
log.LogErrorFrom("Run", "recordContainerInfo", err)
return
}
// 发送用户的命令
sendUserCommand(cmdArray, pipeWriter)
// 创建cgroup manager并通过调用set和apply设置资源限制并在容器上生效
cgroupManager := cgroups.NewCgroupManager(cgroupName)
containerCM := cgroups.NewCgroupManager(cgroupName + "_" + cId)
// 设置资源限制
cgroupManager.Set(res)
containerCM.Set(res)
// 将容器进程加入到各个子系统中
cgroupManager.Apply(parent.Process.Pid)
containerCM.Apply(parent.Process.Pid)
// 等待结束
if tty {
// 如果是detach模式的话就父进程不需要等待子进程结束,而是启动子进程后自行结束就可以了
if err := parent.Wait(); err != nil {
log.Log.Error(err)
}
cgroupManager.Destroy()
containerCM.Destroy()
// 删除设置的AUFS工作目录
rootUrl := "./"
mntUrl := "./mnt"
DeleteWorkSpace(rootUrl, mntUrl, volume)
rootUrl := "/var/lib/mydocker/aufs/"
mntUrl := filepath.Join(rootUrl, "mnt", cId)
DeleteWorkSpace(rootUrl, mntUrl, volume, cId)
DeleteContainerInfo(containerInfo)
os.Exit(1)
}
Expand Down

0 comments on commit c89c49d

Please sign in to comment.