Running Jenkins in Docker
Tags: jenkins, devops, docker
Published: 2021-03-04 17:43:28

ขั้นตอนการเซ็ตอัพ Jenkins Server โดยให้ Docker ทำหน้าที่เป็น Cloud ใช้สำหรับการจัดการกับ Node ที่สร้างขึ้นมาจาก Docker Image ที่เตรียมไว้ใช้สำหรับ Build, Push Docker Image และวิธีการเขียนและรัน Pipeline บน VSCode _(ขั้นตอนทั้งหมดนี้ทำบนเครื่อง macOS)_ 

#เซ็ตอัพ Jenkins Server

1) สร้าง `jenkins` directory แล้วเปิด vscode
```
mkdir jenkins
cd jenkins
code .
```

2) สร้างไฟล์ `Dockerfile` เพื่อใช้สำหรับ Build docker image เป็น node ให้ Jenkins
```
FROM jenkins/jenkins

USER root
RUN apt-get update -qq && apt-get install -qqy \
    apt-transport-https \
    ca-certificates \
    curl \
    lxc \
    iptables
    
RUN curl -sSL https://get.docker.com/ | sh

RUN usermod -aG docker jenkins

CMD dockerd
```

3) Build docker image แล้ว push ไปที่ docker hub _(เปลี่ยน codebangkok เป็นชื่อ docker hub username ของคุณเอง)_
```
docker image build -t codebangkok/jkdocker .
docker image push codebangkok/jkdocker
```

4) สร้างไฟล์ `docker-compose.yml` เพื่อใช้รัน Jenkin Container พอร์ท 8080 และ socat ใช้สำหรับการ Expose docker daemon พอร์ท 2375
```
version: '3.9'
services:

  jenkins:
    container_name: jenkins
    image: jenkins/jenkins
    ports:
      - 8080:8080
    volumes:
      - ${PWD}/jenkins_home:/var/jenkins_home

  socat:
    container_name: socat
    image: alpine/socat
    ports:
      - 2375:2375
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: tcp-listen:2375,fork,reuseaddr unix-connect:/var/run/docker.sock
```

4) รัน docker-compose
```
docker-compose up -d
```

6) รอซักพัก แล้วเข้าไปดู log ใน container จนกว่าจะมี initialAdminPassword ขึ้นมาให้เรา copy เก็บเอาไว้
```
docker container logs jenkins
```
![initialAdminPassword](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwlslkzn5qofjq53uyox.png)

7) เปิด Browser แล้วเข้าไปที่ [http://localhost:8080](http://localhost:8080) แล้วใส่ initialAdminPassword 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lpczpctyioloynsh87gg.png)

8) Install suggested plugins แล้วรอจนกว่าจะติดตั้ง Plugins เสร็จ
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pxhzrc6csn3ppz1o9oa6.png)

9) Create First Admin User 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fof2o0njyecl855usnjk.png)

10) Instance Configuration กด Save and Finish แล้วกด Start using Jenkins
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7t6qishnkxk2e2idqia1.png)

11) กดเมนู Manage Jenkins -> Manage Plugins -> Available แล้วค้นหาคำว่า Docker แล้วคลิ๊กเพื่อติดตั้ง 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c3tw0pz085x1y9hc8ilw.png)

12) คลิ๊ก Restart Jenkins when installation is complete and no jobs are running แล้วให้กลับไปที่หน้าแรก [http://localhost:8080](http://localhost:8080) เพื่อ login เข้าสู่ระบบใหม่
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xghaf1chppzm5jiybse9.png)

13) กดเมนู Manage Jenkins -> Manage Nodes and Clouds -> Configure Clouds -> Add a new cloud -> Docker
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnuxn2c9976ucnb8lcx0.png)

14) กดปุ่ม Docker Cloud details... กำหนดค่า แล้วกดปุ่ม Test Connection เพื่อทดสอบ
* Docker Host URI = `tcp://host.docker.internal:2375`
* Enabled
* Expose DOCKER_HOST
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wopa9gs5wed4cxsmucdu.png)

15) กดปุ่ม Docker Agent templates -> Add Docker Template -> Container settings... กำหนดค่า แล้วกดปุ่ม Save _(เปลี่ยน codebangkok เป็นชื่อ docker hub username ของคุณเอง)_
* Labels = `docker`
* Enabled
* Name = `docker`
* Docker Image = `codebangkok/jkdocker`
* Volumes = `/var/run/docker.sock:/var/run/docker.sock`
* Run container privileged
* Remote File System Root = `/var/jenkins_home`
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zajc2yggu82902jbdnwn.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtwu4xrcmt7ee0bcd2xu.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zg5zxer3hsu949rv9ba9.png)

#วิธีการเขียนและรัน Pipeline บน VSCode
1) เข้าเวป [http://localhost:8080](http://localhost:8080)
2) คลิ๊กเมนู New Item แล้วกำหนดค่า Enter an item name = `PipelineApp` เลือก Pipeline แล้วกดปุ่ม OK -> Save
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emugh40hg3vqh04hxciq.png)

3) ไปที่ VSCode ติดตั้ง Extensions
* [Jenkins Runner](https://marketplace.visualstudio.com/items?itemName=dave-hagedorn.jenkins-runner)
* [JenkinsFile Support](https://marketplace.visualstudio.com/items?itemName=ivory-lab.jenkinsfile-support)

4) สร้าง `.vscode` directory แล้วสร้างไฟล์ `settings.json` ด้านในแล้วพิมพ์ตั้งค่าดังนี้ _(เปลียน username, password เป็นของคุณเอง)_

```
{
    "jenkins-runner.jobs": {
        "PipelineApp": {
            "isDefault": true,
            "runWith": "localhost",
            "name": "PipelineApp"
        }
    },
    "jenkins-runner.hostConfigs": {
        "localhost": {
            "url": "http://localhost:8080",
            "user": "admin",
            "password": "admin",
            "useCrumbIssuer": true
        }
    }
}
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yarwfcw9a29ebv2izqns.png)

5) สร้างไฟล์ `Jenkinsfile` แล้วใส่โค้ด Hello World Pipeline
```
node {
    stage("Hello") {
        echo "Hello World"
    }
}
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iztgi5d3hwnm2rp60cqe.png)

6) ทดลองรัน Pipeline ด้วยการกด Cmd + Shift + P (Command Palette) ค้นหาคำว่า jenkins แล้วเลือก `Run Pipeline Script On Default Job`
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1z9gmhnkg6ixme81yoqp.png)

7) สังเกตุ OUTPUT ว่า Pipeline ได้เริ่มทำงานแล้วพร้อมกับพิมพ์คำว่า `Hello World` ออกมาที่ Stage `Hello` ด้วย
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7putb6n0afb1dlgcueaa.png)

8) แก้ไขไฟล์ Jenkinsfile โดยจะเพิ่ม Stage ดังนี้ _(เปลี่ยน codebangkok เป็นชื่อ docker hub username และ password ของคุณเอง)_
* **SCM** ใช้สำหรับ git clone source code ลงมาที่ workspace
* **Build** ใช้สำหรับ build docker image ด้วย Dockerfile ในโปรเจ็ค
* **Push** ใช้สำหรับ push docker image ไปที่ docker registry 
```
node("docker") {
    stage("SCM") {
        git branch: "master", url: "https://github.com/codebangkok/jenkins"
    }
    stage("Build") {
        sh "docker image build -t codebangkok/goapp src/goapp"
    }
    stage("Push") {
        sh """
        docker login -u "codebangkok" -p "mypassword"
        docker image push codebangkok/goapp
        """
    }
}
```

9) ทดสอบรัน Pipeline อีกครั้ง ด้วยการกด Cmd + Shift + P (Command Palette) แล้วเลือก `Run Pipeline Script On Default Job` สังเกตุว่า Jenkins จะสร้าง docker container ขึ้นมาเพื่อใช้สำหรับ build docker image
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mpvcsapdbyfqtkdztrd.png)

และหลังจาก Build เสร็จแล้วก็จะทำการ Push ไปที่ Docker registry แล้วก็จบ Pipeline
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2embiidorbf32sd5vuv0.png)

10) กลับไปที่เวป [http://localhost:8080/job/PipelineApp](http://localhost:8080/job/PipelineApp) ดูที่ Stage View แสดงให้เห็นถึงการทำงานของ Pipeline ที่ Success ทุกขั้นตอน
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eush0899lwdsv6vp9ltt.png)