Docker: запуск Jenkins в Docker и Docker private registry

Автор: | 23/11/2017
 

Задача – развернуть Jenkins и Docker Private registry в Китае, на AWS.

Аналогичные посты – тут>>> и тут>>>, только в этом посте всё собрано в одно целое и упорядочено.

К EC2 будут подключены два EBS – один с данными Jenkins, второй – с данными Docker.

Для Docker registry в качестве хранилища используем AWS S3.

Далее в посте:

  1. создадим два EBS
  2. запустим EC2 в Китае
  3. установим Docker, Docker Compose
  4. подключим EBS, один в /jenkins, второй в /docker
  5. запустим Jenkins из Docker Compose, к которому будет монтироваться /jenkins
  6. создадим S3 корзину для образов Registry
  7. запустим Docker registry из Docker Compose, к которому будет монтироваться /docker

Подготовка

AWS EC2

Запускаем EC2:

EBS

В том же регионе (cn-north-1b) создаём два EBS по 8 GB:

Подключаем их к EC2:

Логинимся на инстанс, проверяем:

[simterm]

$ 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   8G  0 disk 
xvdc    202:32   0   8G  0 disk 

[/simterm]

Создаём файловые системы на разделах:

[simterm]

# sgdisk -n 1 /dev/xvdb 
Creating new GPT entries.
The operation has completed successfully.

[/simterm]

И для Docker:

[simterm]

# sgdisk -n 1 /dev/xvdc
Creating new GPT entries.
The operation has completed successfully.

[/simterm]

Проверяем:

[simterm]

# 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   8G  0 disk 
└─xvdb1 202:17   0   8G  0 part 
xvdc    202:32   0   8G  0 disk 
└─xvdc1 202:33   0   8G  0 part

[/simterm]

Создаём файловые системы:

[simterm]

# mkfs.ext4 /dev/xvdb1 
# mkfs.ext4 /dev/xvdc1

[/simterm]

Создаём точки монтирования:

[simterm]

# mkdir /jenkins
# mkdir /docker

[/simterm]

Находим UUID разделов:

[simterm]

# blkid /dev/xvdb1
/dev/xvdb1: UUID="4da7fd0f-13a9-434b-8d33-f23776abf5f0" TYPE="ext4" PARTUUID="3a264f88-917e-47a9-a966-4daf100965d7"
# blkid /dev/xvdc1 
/dev/xvdc1: UUID="0b371143-d16a-40a2-9f35-2151952566b2" TYPE="ext4" PARTUUID="98178e64-4e95-42e9-baac-31700463cf6e"

[/simterm]

Редактируем /etc/fstab (с nofail опцией):

LABEL=cloudimg-rootfs   /        ext4   defaults,discard        0 0
UUID=4da7fd0f-13a9-434b-8d33-f23776abf5f0 /jenkins ext4 defaults,nofail 0 2
UUID=0b371143-d16a-40a2-9f35-2151952566b2 /docker ext4 defaults,nofail 0 2

Монтируем все разделы из /etc/fstab:

[simterm]

# mount -a

[/simterm]

Проверяем:

[simterm]

# 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   8G  0 disk 
└─xvdb1 202:17   0   8G  0 part /jenkins
xvdc    202:32   0   8G  0 disk 
└─xvdc1 202:33   0   8G  0 part /docker

[/simterm]

Docker

Обновляем пакеты:

[simterm]

# apt update && apt upgrade

[/simterm]

Устанавливаем Docker:

[simterm]

# curl https://get.docker.com/ | bash

[/simterm]

Docker Compose:

[simterm]

# curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

[/simterm]

Пока устанавливается Compose – обновляем настройки для Docker – меняем data-root.

Создаём файл /etc/docker/daemon.json:

{
  "data-root": "/docker/"
}

Перезапускаем docker демон:

[simterm]

# systemctl restart docker.service

[/simterm]

Проверяем содержимое /docker:

[simterm]

# ls -l /docker/
total 64
drwx------ 2 root root  4096 Nov 22 11:16 builder
drwx--x--x 3 root root  4096 Nov 22 11:16 containerd
drwx------ 2 root root  4096 Nov 22 11:16 containers
...

[/simterm]

Проверяем статус docker:

[simterm]

# docker info | grep Root
Docker Root Dir: /docker

[/simterm]

Systemd

Что бы Docker демон запускался после того, как будут смонтирован каталог /docker – обновляем systemd-сервис docker.service.

Проверяем статус точки монтирования /docker:

[simterm]

# systemctl status docker.mount
● docker.mount - /docker
   Loaded: loaded (/proc/self/mountinfo)
   Active: active (mounted) since Wed 2017-11-22 11:12:43 UTC; 5min ago
    Where: /docker
     What: /dev/xvdc1
    Tasks: 0
   Memory: 0B
      CPU: 0

[/simterm]

В файле /lib/systemd/system/docker.service в блоке [Unit] в конец строки After добавляем docker.mount:

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service docker.mount
Wants=network-online.target
Requires=docker.socket
...

Перезагружаем машину, что бы убедиться, что всё работает:

[simterm]

# reboot

[/simterm]

После рестарта – проверяем ещё раз:

[simterm]

# docker info | grep Root
Docker Root Dir: /docker

[/simterm]

Всё гуд.

Jenkins

Создаём Compose файл:

version: '3'

services:
  jenkins:
    user: root
    image: "daocloud.io/jenkins"
    restart: "always"
    ports:
      - "80:8080"
    volumes:
     - /jenkins/:/jenkins
     - /var/run/docker.sock:/var/run/docker.sock
     - /usr/bin/docker:/usr/bin/docker
    environment:
     - JENKINS_HOME=/jenkins

Используем daocloud.io., что бы не тянуть из hub.docker.com (China ведь).

Запускаем:

[simterm]

# docker-compose up -d
Starting jenkins_jenkins_1 
Starting jenkins_jenkins_1 ... done

[/simterm]

Проверяем содержимое /jenkins:

[simterm]

# ls -l /jenkins/
total 92
-rw-r--r--  1 root root  1592 Nov 22 11:40 config.xml
-rw-r--r--  1 root root    29 Nov 22 11:40 failed-boot-attempts.txt
-rw-r--r--  1 root root   159 Nov 22 11:40 hudson.model.UpdateCenter.xml
-rw-------  1 root root  1712 Nov 22 11:38 identity.key.enc
drwxr-xr-x  2 root root  4096 Nov 22 11:38 init.groovy.d
...

[/simterm]

Тут пока всё готово.

Docker Registry

S3 для registry storage

Для registry – нам потребуется S3 корзина для хранения образов и SSL сертификат. Запуск Docker registry описан тут>>>.

Создаём корзину:

[simterm]

$ aws s3api --profile tag-cn create-bucket --bucket tag-ci-registry --region cn-north-1 --create-bucket-configuration LocationConstraint=cn-north-1

[/simterm]

Создаём файл политик доступа:

{ 
  "Version": "2012-10-17",
  "Statement": [
    { 
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": "arn:aws-cn:s3:::tag-ci-registry"
    },
    { 
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListMultipartUploadParts",
        "s3:AbortMultipartUpload"
      ],
      "Resource": "arn:aws-cn:s3:::tag-ci-registry/*"
    }
  ]
}

Подключаем его к корзине:

[simterm]

$ aws s3api --profile tag-cn put-bucket-policy --bucket tag-ci-registry --policy file://tag-ci-registry-policy.json

[/simterm]

SSL

Следующим шагом – требуется добавить SSL сертификат, что бы Docker registry работал через внешний домен.

Создаём каталог для сертификата:

[simterm]

# mkdir -p /etc/ssl/cn/2017

[/simterm]

С рабочей машины загружаем сертификат и ключ:

[simterm]

$ scp -i ../../tag-server-api-infrastructure/.ssh/tag-cn.pem ssl_certificate.cer ubuntu@52.***.***.9:/home/ubuntu
$ scp -i ../../tag-server-api-infrastructure/.ssh/tag-cn.pem star_cn-private-key_2017.key ubuntu@52.***.***.9:/home/ubuntu

[/simterm]

На CI хосте перемещаем их:

[simterm]

# mv ssl_certificate.cer /etc/ssl/cn/2017
# mv star_cn-private-key_2017.key /etc/ssl/cn/2017

[/simterm]

В файл /etc/docker/daemon.json добавляем параметр insecure-registries:

{
  "data-root": "/docker/",
  "insecure-registries": ["registry-domain.cn:5000"]
}

HTTP авторизация

Создаём каталог для файла htpasswd:

[simterm]

# mkdir -p /etc/registry/auth

[/simterm]

И сам файл с пользователем и паролем, который будет использоваться для авторизации на Docker registry:

[simterm]

# docker run --entrypoint htpasswd daocloud.io/registry:2 -Bbn dockeruser dockerpass > /etc/registry/auth/htpasswd

[/simterm]

Docker registry config.yml

Следующим шагом – надо переопределить параметры storage в /etc/docker/registry/config.yml внутри контейнера с registry.

Создаём каталог:

[simterm]

# mkdir /etc/registry/conf

[/simterm]

В нём создаём файл config.yml, в котором переопределяяем параметры в блоке storage:

version: 0.1
log:
  fields:
    service: registry
storage:
  s3:
    accesskey: AKI***L5A
    secretkey: OtN***7Cb
    region: cn-north-1
    bucket: tag-ci-registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

Docker Compose для Docker Registry

Создаём файл, в котором указываем все каталоги для монтирования в контейнер и переменные для S3 и SSL:

registry:
  restart: always
  image: daocloud.io/registry:2
  ports:
    - 5000:5000
  environment:
    AWS_ACCESS_KEY_ID: AKI***L5A
    AWS_SECRET_ACCESS_KEY: OtN***7Cb
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/ssl_certificate.cer
    REGISTRY_HTTP_TLS_KEY: /certs/star_cn-private-key_2017.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /etc/ssl/cn/2017:/certs
    - /etc/registry/auth:/auth
    - /etc/registry/conf/config.yml:/etc/docker/registry/config.yml

Запускаем:

[simterm]

# docker-compose up -d
Starting registry_registry_1 ... 
Starting registry_registry_1 ... done

[/simterm]

Проверяем.

Создадим копию образа из уже скачанных:

[simterm]

# docker tag daocloud.io/registry:2 registry-domain.cn:5000/registry:2

[/simterm]

Пушим его:

[simterm]

# docker push registry-domain.cn:5000/registry:2
The push refers to repository [registry-domain.cn:5000/registry]
3c133a51bc00: Preparing 
a2717186d7dd: Preparing 
656c7684d0bd: Preparing 
7683d4fcdf4e: Preparing 
ef763da74d91: Preparing 
no basic auth credentials

[/simterm]

Гуд – требует авторизацию:

no basic auth credentials

Логинимся:

[simterm]

# docker login registry-domain.cn:5000
Username: dockeruser
Password: 
Login Succeeded

[/simterm]

Пушим:

[simterm]

# docker push registry-domain.cn:5000/registry:2
The push refers to repository [registry-domain.cn:5000/registry]
3c133a51bc00: Pushed 
a2717186d7dd: Pushed 
656c7684d0bd: Pushed 
7683d4fcdf4e: Pushed 
ef763da74d91: Pushed 
2: digest: sha256:435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6 size: 1364

[/simterm]

Проверяем содержимое корзины:

[simterm]

$ aws s3 --profile tag-cn ls --recursive s3://tag-ci-registry
2017-11-22 16:07:22    6265374 docker/registry/v2/blobs/sha256/3a/3a41740f900cbba03b223c025f03632db1284d85a696677bca8d6375cdf6040b/data
2017-11-22 16:07:23       1364 docker/registry/v2/blobs/sha256/43/435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6/data
2017-11-22 16:07:21    2385012 docker/registry/v2/blobs/sha256/49/49388a8c9c86a6f56d228954eede699c64fce6c671a989e3e21c391859694645/data
2017-11-22 16:07:20        213 docker/registry/v2/blobs/sha256/65/65f212f7c77805418cb85461d93a522c1bf4eebac73009ccd23ac2159ac33dad/data
2017-11-22 16:07:23       3165 docker/registry/v2/blobs/sha256/a0/a07e3f32a779aa924fd47f6797d4d5c93061c50c0eb97d464f08365a3a30200b/data
2017-11-22 16:07:20        371 docker/registry/v2/blobs/sha256/e1/e16ef4b766841014d6a902f034f0f67698bcbebc1b4c36ff3574d3730e79e2ee/data
...

[/simterm]

Всё отлично.

Docker Compose старт после reboot сервера

Сначала решил добавить systemd-юнит, как описано тут>>>, но – не потребовалось: хватает опции restart: "always" в docker-compose.yml обоих сервисов.

Проверяем:

[simterm]

ubuntu@ip-172-31-2-146:~$ sudo docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                             NAMES
6ab55042e375        daocloud.io/jenkins      "/bin/tini -- /usr/l…"   2 minutes ago       Up 2 minutes        50000/tcp, 0.0.0.0:80->8080/tcp   jenkins_jenkins_1
f8d70fafbe68        daocloud.io/registry:2   "/entrypoint.sh /etc…"   About an hour ago   Up 2 minutes        0.0.0.0:5000->5000/tcp            registry_registry_1

[/simterm]

Перезагружаем сервер:

[simterm]

ubuntu@ip-172-31-2-146:~$ sudo reboot
Connection to 52.***.***.9 closed by remote host.
Connection to 52.***.***.9 closed.

[/simterm]

После рестарта – логинимся, проверяем ещё раз:

[simterm]

...
Last login: Wed Nov 22 14:36:07 2017 from 54.***.***.0
ubuntu@ip-172-31-2-146:~$ sudo docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                             NAMES
6ab55042e375        daocloud.io/jenkins      "/bin/tini -- /usr/l…"   3 minutes ago       Up 12 seconds       50000/tcp, 0.0.0.0:80->8080/tcp   jenkins_jenkins_1
f8d70fafbe68        daocloud.io/registry:2   "/entrypoint.sh /etc…"   About an hour ago   Up 12 seconds       0.0.0.0:5000->5000/tcp            registry_registry_1

[/simterm]

Готово.

По хорошему – сюда бы ещё добавить NGINX на входе, что бы подключить SSL для Jenkins – но уже не сегодня.