Post

Docker CI ์•Œ์•„๋ณด๊ธฐ

KANS ์Šคํ„ฐ๋”” 1์ฃผ์ฐจ Docker CI ์•Œ์•„๋ณด๊ธฐ

๐Ÿ’ก KANS ์Šคํ„ฐ๋””
CloudNet์—์„œ ์ฃผ๊ด€ํ•˜๋Š” KANS(Kubernetes Advanced Networking Study)์œผ๋กœ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋„คํŠธ์›Œํ‚น ์Šคํ„ฐ๋””์ž…๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ๊ธ€์€ ์Šคํ„ฐ๋””์˜ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์Šคํ„ฐ๋””์— ๊ด€์‹ฌ์ด ์žˆ์œผ์‹  ๋ถ„์€ CloudNet Blog๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

๋“ค์–ด๊ฐ€๋ฉฐ

์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์šด์˜ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, CI ๊ณผ์ •์—์„œ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ๋นŒ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” Docker ๊ธฐ๋ฐ˜์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณธ ํ›„ ์ง์ ‘ ์ž‘์—…ํ•ด๋ณด๋ฉฐ ๋นŒ๋“œ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์ ์„ ์‚ดํŽด๋ณธ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ Kaniko์™€ ๊ฐ™์€ ๋Œ€์ฒด๋ฐฉ์•ˆ์„ ์‚ดํŽด๋ณด๋ฉฐ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค.

Docker CI

Docker CI๋ฅผ ์œ„ํ•ด์„  ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ Docker๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด DinD(Docker in Docker) ํ˜น์€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ๋„ docker๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ socket ๋งˆ์šดํŠธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

DinD๋Š” ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ Docker ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, GitLab Runner๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ๊ธฐ๋ฐ˜์ด๋‹ค.

docker๋Š” socket ๊ธฐ๋ฐ˜์œผ๋กœ ํ†ต์‹ ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— docker๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” socket์„ ๋งˆ์šดํŠธํ•˜๋ฉด, docker์™€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ์„  docker.socket์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž.

socket

docker๋Š” Unix domain-socket์„ ์‚ฌ์šฉํ•œ๋‹ค. socket์„ ํ†ตํ•ด docker daemon๊ณผ ํ†ต์‹ ํ•˜๋ฏ€๋กœ Host์— ์žˆ๋Š” docker socket์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ๋„ docker๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

tcp๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ์„ค์ •ํ•˜๋ฉด, ๋‹ค๋ฅธ ์„œ๋ฒ„์—์„œ๋„ docker ๋ฐ๋ชฌ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ k8s๋ผ๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

1
2
3
4
lsof /run/docker.sock
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
systemd    1 root   40u  unix 0xffff966d51faddc0      0t0 31848 /run/docker.sock type=STREAM
dockerd 3966 root    4u  unix 0xffff966d51faddc0      0t0 31848 /run/docker.sock type=STREAM

DinD vs mount docker.socket

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ DinD์™€ socket ๋งˆ์šดํŠธ ๋ฐฉ์‹์„ ๋น„๊ตํ•˜์ž. docker community์™€ jpetazzo Post์—์„œ DinD์™€ docker.socket mount์˜ ์ฐจ์ด๊ฐ€ ์ž˜ ์ •๋ฆฌ๋˜์–ด์žˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์š”์•ฝํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. --privileged ๋กœ ์ธํ•ด SElinux์™€ ๊ฐ™์€ ๋ณด์•ˆ ๋ชจ๋“ˆ๊ณผ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. ๋‚ด๋ถ€ Docker๋Š” COW ์‹œ์Šคํ…œ ์œ„์—์„œ ์‹คํ–‰๋˜๊ธฐ์— ์—ฌ๋Ÿฌ ํŒŒ์ผ์‹œ์Šคํ…œ ์กฐํ•ฉ์ด ์ž‘๋™๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

    ex) AUFS on top of AUFS

  3. ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•ด๋„, Host Docker์—์„œ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์บ์‹œ ํšจ์œจ์„ฑ์ด ๋†’์•„์ง„๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ ๋ณด์•ˆ์ƒ, ์„ฑ๋Šฅ ์ฐจ์›์—์„œ DinD๋ณด๋‹ค socket mount ๋ฐฉ์‹์ด ๊ฐ•๋ ฅํžˆ ์ถ”์ฒœ๋œ๋‹ค. ์ด์ œ socket mount ๋ฐฉ์‹์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ์•ˆ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

์‹ค์Šต

ํ™˜๊ฒฝ

Jenkins๋ฅผ ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ€๋™ํ•˜๊ณ , ํ•ด๋‹น ์ปจํ…Œ์ด๋„ˆ์— socket์„ ๋งˆ์šดํŠธํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•œ๋‹ค.

  • Jenkins ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
1
docker run -d -p 8080:8080 -p 50000:50000 --name jenkins-server --restart=on-failure -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker jenkins/jenkins

์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด Init ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

1
docker exec -it jenkins-server cat /var/jenkins_home/secrets/initialAdminPassword
  • docker api ๊ฐ€๋Šฅ์—ฌ๋ถ€ ํ™•์ธ

docker api ์‚ฌ์šฉ๊ฐ€๋Šฅ์—ฌ๋ถ€ ํ™•์ธํ•ด๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

1
2
3
docker exec -it --user 0 jenkins-server docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS          PORTS                                                                                      NAMES
86170af3b1ab   jenkins/jenkins   "/usr/bin/tini -- /uโ€ฆ"   44 seconds ago   Up 43 seconds   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp   jenkins-server
  • ์ด์ œ root๊ฐ€ ์•„๋‹Œ jenkins ์œ ์ €๋„ docker๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•œ๋‹ค.
1
2
3
4
docker exec -it --user 0 jenkins-server bash
root@8266345972f8:/# groupadd -for -g $(stat -c '%g' /var/run/docker.sock) docker
root@8266345972f8:/# usermod -aG docker jenkins

์‹ค์ œ Jenkins ์œ ์ €๋กœ ์ ‘์†ํ•˜์—ฌ API๋ฅผ ํ™•์ธํ•œ๋‹ค.

1
2
3
docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED       STATUS       PORTS                                                                                      NAMES
8266345972f8   jenkins/jenkins   "/usr/bin/tini -- /uโ€ฆ"   1 hours ago   Up 1 hours   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp   jenkins-server

์„œ๋ฒ„์˜ IP์ฃผ์†Œ์˜ 8080 ํฌํŠธ๋กœ ์ ‘์†ํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด Jenkins๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

image.png

์  ํ‚จ์Šค CI

docker pipeline pulgin์„ ์„ค์น˜ ํ›„ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“ ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
pipeline {
    agent any

    stages {
        stage('Create Dockerfile and a.txt') {
            steps {
                script {
                    // a.txt ํŒŒ์ผ ์ƒ์„ฑ
                    writeFile file: 'a.txt', text: "This is a simple file."
                    
                    // ๊ฐ„๋‹จํ•œ Dockerfile ์ƒ์„ฑ
                    writeFile file: 'Dockerfile', text: """
                    FROM alpine:latest
                    COPY a.txt /a.txt
                    CMD ["cat", "/a.txt"]
                    """
                }
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    // Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ
                    docker.build("simple-jenkins-image")
                }
            }
        }

        stage('Run Docker Image') {
            steps {
                script {
                    // ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์‹คํ–‰ํ•˜๊ณ  a.txt ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ์ถœ๋ ฅ
                    docker.image("simple-jenkins-image").inside {
                        sh 'cat /a.txt'
                    }
                }
            }
        }
    }

    post {
        always {
            echo 'Cleaning up...'
            // ํ•„์š” ์‹œ ํด๋ฆฐ์—… ์ฝ”๋“œ ์ถ”๊ฐ€
        }
        success {
            echo 'Build completed successfully!'
        }
        failure {
            echo 'Build failed!'
        }
    }
}

Job์„ ์‹œ์ž‘ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž˜ ๋™์ž‘ํ•œ๋‹ค.

image.png

๋˜, ํ˜ธ์ŠคํŠธ ์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
root@MyServer:~# docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
simple-jenkins-image   latest    f6943e3910a7   59 seconds ago   7.8MB
jenkins/jenkins        latest    fd13cb1b7315   2 days ago       471MB
alpine                 latest    324bc02ae123   5 weeks ago      7.8MB

๋Œ€์•ˆ ๋ฐฉ๋ฒ•

์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ๋นŒ๋“œ์— Docker ์ž์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์ด ์กด์žฌํ•œ๋‹ค.

kaniko

DinD๋Š” ๋„์ปค์ž์ฒด์—์„œ sudo ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ, ์ปจํ…Œ์ด๋„ˆ๊ฐ€ root ๊ถŒํ•œ์„ ๊ฐ€์ง€๋ฉฐ ์ด๋Š” ๋ณด์•ˆ์ƒ ์ข‹์ง€ ์•Š๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Kaniko๊ฐ€ ๋‚˜์˜ค๊ฒŒ๋œ๋‹ค. ๋ฃจํŠธ ๊ถŒํ•œ์—†์ด, ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ด๋‹ค. ๊ตฌ๊ธ€์—์„œ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ, ์œˆ๋„์šฐ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.

kaniko-arch.webp

์ถœ์ฒ˜: https://blog.nashtechglobal.com/deep-dive-into-kaniko-understanding-the-architecture-and-workflow/

GitLab์œผ๋กœ CI/CD๋ฅผ ๊ตฌ์„ฑํ–ˆ์„ ๋•Œ, DinD๋Š” root ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์—ฌ ๋ณ„๋„๋กœ Runner์— privileged ์˜ต์…˜์„ ํ—ˆ์šฉํ•ด์ค˜์•ผ ํ–ˆ๋‹ค. ์ด๋Š” ๋ณด์•ˆ์ƒ ์ข‹์ง€ ์•Š๊ณ , GitLab์—์„œ๋„ ์ด๋ฅผ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ณ„๋„๋กœ Kaniko์— ๋Œ€ํ•œ GitLab ๊ฐ€์ด๋“œ ๋ฌธ์„œ๊ฐ€ ์žˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๋นŒ๋“œ๋„๊ตฌ๋กœ๋Š” Buildkit, Buildah๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. ํ•ด๋‹น ํฌ์ŠคํŠธ์—์„œ ์ž˜ ์ •๋ฆฌ๋˜์–ด์žˆ๋‹ค.

This post is licensed under CC BY 4.0 by the author.