Продолжение развёртывания CI инфрастуктуры в Китае. Начало тут>>>.
В предыдущей части – запустили Docker registry, теперь – нужен Jenkins, который будет собирать образы сервисов и пушить в это хранилище.
Для запуска Jenkins потребуется:
- создать EBS для Jenkins workspaces и подключить к EC2
- создать EBS для Docker образов
- запустить и проверить Jenkins
- проверить Docker билды в Jenkins
- подготовить Docker Compose файл для его запуска в дальнейшем
Подготовка EC2 и EBS для Jenkins
EC2 уже есть. Добавляем к нему Elastic Block Storage, в той же Availability Zone, что и EC2:
aws ec2 describe-instances --instance-ids i-01edb17886a20b25d --query 'Reservations[*].Instances[*].Placement.AvailabilityZone' --output text
cn-north-1b
Создаём EBS:
aws ec2 create-volume --size 80 --region cn-north-1 --availability-zone cn-north-1b --volume-type gp2
{
"AvailabilityZone": "cn-north-1b",
"Encrypted": false,
"VolumeType": "gp2",
"VolumeId": "vol-0867c0e01f2ddf30b",
"State": "creating",
"Iops": 240,
"SnapshotId": "",
"CreateTime": "2017-06-22T11:26:31.289Z",
"Size": 80
}
Добавляем теги:
aws ec2 create-tags --resources vol-0867c0e01f2ddf30b --tags Key=Name,Value=tag-cn-jenkins-workspaces
Подключаем его к EC2.
Проверяем имеющиеся разделы:
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
Подключаем:
aws ec2 attach-volume --volume-id vol-0867c0e01f2ddf30b --instance-id i-01edb17886a20b25d --device /dev/xvdb
{
"AttachTime": "2017-06-22T11:28:16.640Z",
"InstanceId": "i-01edb17886a20b25d",
"VolumeId": "vol-0867c0e01f2ddf30b",
"State": "attaching",
"Device": "/dev/xvdb"
}
На интансе проверяем:
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 80G 0 disk
Создаём новый раздел на устройстве xvdb
:
sudo fdisk /dev/xvdb
Welcome to fdisk (util-linux 2.27.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xe23e34c2.
Command (m for help): n
Partition type
p primary (0 primary, 0 extended, 4 free)
e extended (container for logical partitions)
Select (default p):
Using default response p.
Partition number (1-4, default 1):
First sector (2048-167772159, default 2048):
Last sector, +sectors or +size{K,M,G,T,P} (2048-167772159, default 167772159):
Created a new partition 1 of type 'Linux' and of size 80 GiB.
Command (m for help): p
Disk /dev/xvdb: 80 GiB, 85899345920 bytes, 167772160 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xe23e34c2
Device Boot Start End Sectors Size Id Type
/dev/xvdb1 2048 167772159 167770112 80G 83 Linux
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
Проверяем:
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 80G 0 disk
└─xvdb1 202:17 0 80G 0 part
Создаём файловую систему:
sudo mkfs.ext4 /dev/xvdb1
Создаём точку монтирования, монтируем раздел:
sudo mkdir /jenkins
sudo mount /dev/xvdb1 /jenkins/
ls -l /jenkins/
total 16
drwx------ 2 root root 16384 Jun 22 11:33 lost+found
Обновляем /etc/fstab
, добавляем монтирование при старте машины.
Находим UUID раздела:
sudo blkid /dev/xvdb1
/dev/xvdb1: UUID="9d0efa97-c292-479d-b2e3-e83a3ab8d40f" TYPE="ext4" PARTUUID="e23e34c2-01"
Редактируем /etc/fstab
(с nofail
опцией):
cat /etc/fstab
LABEL=cloudimg-rootfs / ext4 defaults,discard 0 0
UUID=9d0efa97-c292-479d-b2e3-e83a3ab8d40f /jenkins ext4 defaults,nofail 0 2
Готово.
EBS для Docker образов
Немного забегая наперёд – сразу добавим раздел для образов Docker.
Создаём EBS аналогично разделу для Jenkins:
aws ec2 create-volume --size 80 --region cn-north-1 --availability-zone cn-north-1b --volume-type gp2
aws ec2 create-tags --resources vol-0f756a2781a4cdc40 --tags Key=Name,Value=tag-cn-docker-lib
aws ec2 attach-volume --volume-id vol-0f756a2781a4cdc40 --instance-id i-01edb17886a20b25d --device /dev/xvdc
echo ';' | sudo sfdisk /dev/xvdc
sudo mkfs.ext4 /dev/xvdc1
sudo mkdir /docker
sudo mount /dev/xvdc1 /docker/
ls -l /docker/
total 16
drwx------ 2 root root 16384 Jun 22 13:40 lost+found
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdb 202:16 0 80G 0 disk
└─xvdb1 202:17 0 80G 0 part /jenkins
xvdc 202:32 0 100G 0 disk
└─xvdc1 202:33 0 100G 0 part /docker
Создаём файл для настройки Docker engine:
sudo vim /etc/docker/daemon.json
В котором указываем каталог:
{
"graph": "/docker/"
}
Перезапускаем docker
:
sudo service docker restart
Проверяем:
sudo ls -l /docker/
total 52
drwx------ 5 root root 4096 Jun 22 13:44 aufs
drwx------ 2 root root 4096 Jun 22 13:44 containers
drwx------ 3 root root 4096 Jun 22 13:44 image
drwx------ 2 root root 16384 Jun 22 13:40 lost+found
drwxr-x--- 3 root root 4096 Jun 22 13:44 network
drwx------ 4 root root 4096 Jun 22 13:44 plugins
drwx------ 2 root root 4096 Jun 22 13:44 swarm
drwx------ 2 root root 4096 Jun 22 13:44 tmp
drwx------ 2 root root 4096 Jun 22 13:44 trust
drwx------ 2 root root 4096 Jun 22 13:44 volumes
Запуск Jenkins
Сначала запустим его вручную, через docker run
, потом – добавим Docker Compose файл.
Т.к. это Китай со своими весёлостями в плане скорости – сначала сделем просто pull
(через китайское зеркало DockerHub pull
вообще не прошёл, падал на одном и том же слое с ошибкой 500):
time docker pull jenkins
Using default tag: latest
latest: Pulling from library/jenkins
...
Digest: sha256:1a017bc762d8177347dbccbe258de842015114806110db060d91cff7e8a3264f
Status: Downloaded newer image for jenkins:latest
real 137m43.960s
Пробуем запустить:
docker run -ti -p 80:8080 -v /jenkins/:/jenkins -v /var/run/docker.sock:/var/run/docker.sock -e JENKINS_HOME='/jenkins' jenkins
mkdir: cannot create directory ‘/jenkins/init.groovy.d’: Permission denied
cp: cannot create regular file ‘/jenkins/init.groovy.d/tcp-slave-agent-port.groovy’: No such file or directory
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
Jun 26, 2017 10:40:44 AM Main deleteWinstoneTempContents
WARNING: Failed to delete the temporary Winstone file /tmp/winstone/jenkins.war
Jun 26, 2017 10:40:44 AM org.eclipse.jetty.util.log.JavaUtilLog info
INFO: Logging initialized @595ms
Jun 26, 2017 10:40:44 AM winstone.Logger logInternal
INFO: Beginning extraction from war file
Jun 26, 2017 10:40:44 AM winstone.Logger logInternal
INFO: Winstone shutdown successfully
Jun 26, 2017 10:40:44 AM winstone.Logger logInternal
SEVERE: Container startup failed
java.io.FileNotFoundException: /jenkins/war/META-INF/MANIFEST.MF (No such file or directory)
...
Проверяем пользователя каталога /jenkins
:
ls -l / | grep jenk
drwxr-xr-x 3 root root 4096 Jun 26 10:39 jenkins
Проверяем пользователя в контейнере:
docker run -u jenkins jenkins id
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)
Находим пользователя с UID 1000 на хосте:
cat /etc/passwd | grep 1000
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
Меняем владельца каталога /jenkins
и запускаем:
sudo chown ubuntu:ubuntu /jenkins/
docker run -ti -p 80:8080 -v /jenkins/:/jenkins -v /var/run/docker.sock:/var/run/docker.sock -e JENKINS_HOME='/jenkins' jenkins
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
Jun 26, 2017 10:43:32 AM Main deleteWinstoneTempContents
WARNING: Failed to delete the temporary Winstone file /tmp/winstone/jenkins.war
Jun 26, 2017 10:43:32 AM org.eclipse.jetty.util.log.JavaUtilLog info
INFO: Logging initialized @587ms
Jun 26, 2017 10:43:32 AM winstone.Logger logInternal
INFO: Beginning extraction from war file
...
Отлично – Jenkins стартует.
Docker билд в Jenkins
Что бы использовать Docker в Jenkins-контейнере – можно замапить исполняемый файл докера в сам контейнер:
docker run -ti -p 80:8080 -v /jenkins/:/jenkins -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker:ro -e JENKINS_HOME=’/jenkins’ jenkins
Но так могут вылезти ошибки вроде такой:
docker: error while loading shared libraries: libltdl.so.7: cannot open shared object file: No such file or directory
Или возможны проблемы с доступом к /var/run/docker/sock:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.29/build?buildargs=%7B%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=registry.domain.cn%3A5000%2Fubuntu%3A20&target=&ulimits=null: dial unix /var/run/docker.sock: connect: permission denied
Другой подход – собрать свой образ Jenkins с установленным в нём Docker.
Создаём Dockerfile
:
FROM jenkins
USER root
RUN curl https://get.docker.com/ | bash
RUN usermod -aG docker jenkins
USER jenkins
Собираем:
docker build -t testjenk:1 .
Запускаем:
docker run -ti -p 80:8080 -v /jenkins/:/jenkins -v /var/run/docker.sock:/var/run/docker.sock -e JENKINS_HOME='/jenkins' testjenk:1 bash
Проверяем билд в самом контейнере:
cd /jenkins/workspace/DockerTestJob
docker build -t asdcsdc .
Sending build context to Docker daemon 2.048kB
Step 1/1 : FROM ubuntu
latest: Pulling from library/ubuntu
75c416ea735c: Downloading [> ] 471.9kB/47.1MB
...
Работает.
Проверка Docker билда в Jenkins
Запускаем Jenkins контейнер:
docker run -ti -p 80:8080 -v /jenkins/:/jenkins -v /var/run/docker.sock:/var/run/docker.sock -e JENKINS_HOME='/jenkins' testjenk:1
Создаём тестовую джобу:
![Docker: AWS [China] - Jenkins в Docker](https://rtfm.co.ua/wp-content/uploads/2017/06/tag_cn_ci_1.png)
Создаём скрипт:
![Docker: AWS [China] - Jenkins в Docker](https://rtfm.co.ua/wp-content/uploads/2017/06/tag_cn_ci_3.png)
def dockerBuild () {
stage ('Docker build') {
def appimage = docker.build("ubuntu:${env.BUILD_NUMBER}")
}
}
node {
dockerBuild()
}
Запускаем:
![Docker: AWS [China] - Jenkins в Docker](https://rtfm.co.ua/wp-content/uploads/2017/06/tag_cn_ci_4.png)
ОК – билд работает.
Билд и пуш в Docker registry
Следующим шагом – проверяем работу Jenkins и Docker registry из предыдущей части.
В Credentials – добавляем данные доступа к созданному ранее registry:
![Docker: AWS [China] - Jenkins в Docker](https://rtfm.co.ua/wp-content/uploads/2017/06/tag_cn_ci_5.png)
Обновляем билд скрипт:
def dockerBuild () {
stage ('Docker build') {
docker.withRegistry('https://registry.domain.cn:5000', 'tagcndockerregistry'){
def img = docker.build("ubuntu:${env.BUILD_NUMBER}")
img.push()
}
}
}
node {
dockerBuild()
}
Запускаем билд, проверяем:
…
[DockerTestJob] Running shell script
+ docker build -t registry.domain.cn:5000/ubuntu:15 .
Sending build context to Docker daemon 2.048kB
Step 1/1 : FROM ubuntu
—> d355ed3537e9
Successfully built d355ed3537e9
Successfully tagged registry.domain.cn:5000/ubuntu:15
[Pipeline] dockerFingerprintFrom
[Pipeline] sh
[DockerTestJob] Running shell script
+ docker tag –force=true registry.domain.cn:5000/ubuntu:15 registry.domain.cn:5000/ubuntu:15
unknown flag: –force
See ‘docker tag –help’.
+ docker tag registry.domain.cn:5000/ubuntu:15 registry.domain.cn:5000/ubuntu:15
[Pipeline] sh
[DockerTestJob] Running shell script
+ docker push registry.domain.cn:5000/ubuntu:15
The push refers to a repository [registry.domain.cn:5000/ubuntu]
0566c118947e: Preparing
…
15: digest: sha256:a0ee7647e24c8494f1cf6b94f1a3cd127f423268293c25d924fbe18fd82db5a4 size: 1357
…
[Pipeline] End of Pipeline
Finished: SUCCESS
Проверяем сам образ:
docker run -ti registry.domain.cn:5000/ubuntu:15 whoami
root
Готово.
Docker Compose для Jenkins в Docker
Последний шаг – добавить Docker Compose файл для запуска Jenkins.
Билдим Jenkins с нормальным именем:
docker build -t registry.domain.cn:5000/tagjenkins .
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM jenkins
...
Successfully tagged registry.domain.cn:5000/tagjenkins:latest
Пушим:
docker push registry.domainr.cn:5000/tagjenkins
The push refers to a repository [registry.domain.cn:5000/tagjenkins]
...
Создаём Compose:
version: '3'
services:
jenkins:
image: "registry.domain.cn:5000/testjenk:1"
restart: "always"
ports:
- "80:8080"
volumes:
- /jenkins/:/jenkins
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JENKINS_HOME=/jenkins
maven:
image: "maven:3.3.9-jdk-8"
packer:
image: "hashicorp/packer:light"
terraform:
image: "hashicorp/terraform:light"
aws-cli:
image: "fstab/aws-cli"
Запускаем:
docker-compose up
Creating network "jenkins_default" with the default driver
Pulling maven (maven:3.3.9-jdk-8)...
3.3.9-jdk-8: Pulling from library/maven
Digest: sha256:18e8bd367c73c93e29d62571ee235e106b18bf6718aeb235c7a07840328bba71
Status: Downloaded newer image for maven:3.3.9-jdk-8
Pulling packer (hashicorp/packer:light)...
light: Pulling from hashicorp/packer
Digest: sha256:311761d935d7170c7b38caf71f84a99735648d7153050e87699b4d3e19d9ad14
Status: Downloaded newer image for hashicorp/packer:light
Pulling aws-cli (fstab/aws-cli:latest)...
latest: Pulling from fstab/aws-cli
Digest: sha256:01a8e8ada9585578d59daa01127a83439a70f9de842542801c18c903c52fbb73
Status: Downloaded newer image for fstab/aws-cli:latest
Pulling terraform (hashicorp/terraform:light)...
light: Pulling from hashicorp/terraform
Digest: sha256:1cf62ff7e0bbb1d8aca82a86c498423b3b057c7f18dcf641b4a1ec3720f93471
Status: Downloaded newer image for hashicorp/terraform:light
Creating jenkins_terraform_1 ...
Creating jenkins_aws-cli_1 ...
Creating jenkins_maven_1 ...
Creating jenkins_jenkins_1 ...
Creating jenkins_packer_1 ...
Creating jenkins_terraform_1
Creating jenkins_aws-cli_1
Creating jenkins_maven_1
Creating jenkins_packer_1
Creating jenkins_aws-cli_1 ... done
Attaching to jenkins_terraform_1, jenkins_maven_1, jenkins_packer_1, jenkins_jenkins_1, jenkins_aws-cli_1
...
Проверяем:
curl -s --user user:pass http://build.domain.cn/api/json | jq -r '.jobs[].name'
DockerTestJob
Готово.