GitLab runner CI/CD

GitLab Runner: GitLabでコミットを検知してGitLab Runnerがジョブを実行、ジョブの結果がGitLabから確認できる

### Shared RunnersとSpecific Runners
Shared Runnersはプロジェクトを跨いで共有、Specific Runnerはプロジェクトに紐づけて専用利用

### Executer
ジョブの実行形式
Shell, Docker, Docker Machine, Parallels, VirtualBox, SSH, Kubernetes
-> shellであればGitLab Runnerがインストールされたホスト上でBash等のShellベースでジョブを実行

### パイプライン
パイプライン方式でジョブを実行する

image: python:3.6.5
variables:
  S3_BUCKET: デプロイに使うS3バケット
  AWS_DEFAULT_REGION: ap-northeast-1
unittest:
  stage: test
  script:
    - pip install pytest
    - pip install -r requirements.txt
    - python -m pytest tests/ -v

deploy:
  stage: deploy
  script:
    - pip install awscli
    - pip install -r requirements.txt -t hello_world/build/
    - cd hello_world/*.py hello_world/build/
    - aws cloudformation package --template-file template.yaml --output-template-file output.yaml --s3-bucket ${S3_BUCKET}
    - aws cloudformation deploy --template-file output.yaml --stack-name demo --capabilities CAPABILITY_IAM

重要なのはdeployのところか
なるほど、testも理解しておかないとあかんな…

devブランチにpushされたら…
masterブランチにpushされたら… など設定できる

– service: 対象のジョブ実行中に起動するDockerイメージ, scriptsを実行するためのDockerイメージ servicesで指定されたコンテナはimagesで指定されたコンテナとは別で起動
– only: 対象ジョブの実行を特定のブランチや特定のタグがプッシュされた時に限定することがd系る
– artifact: ジョブの成果物としてジョブを跨いで共有できる
– when: ジョブを実行するタイミング 
L on_success, on_failure, always, on_failure
– environment: 対象のジョブがどの環境へデプロイを行うのか定義できる

なるほど、そろそろ実際にやってみるか

gitlab入門

stage: パイプラインを制御するためのもので、job毎に使用できるステージを定義するもの。dependenciesとセットになる同じステージのjobは並行で実行される

artifacts: 成果物のパスを指定。特定のjobから成果物をDLできる。後続のjobで引き継がせることができる。ディレクトリのみ。

image: sample.gitlab.com/vagrant/sample/app
stages:
  - test
  - build_static
rspec:
  stage: test
  services:
    - mysql:5.7
  variables:
    MYSQL_DATABASE: sample_test
    MYSQL_ROOT_PASSWORD: mysql_strong_password
  before_script:
    - "echo \"install: --no-document\" > ~/.gemrc"
    - "echo \"update: --no-document\" > ~/.gemrc"
    - bundle install --jobs=4
    - buncle exec rake db:setup RAILS_ENV=test
    - buncle exec rake db:migrate RAILS_ENV=test
  script:
    - bundle exec rspec
  artifacts:
    paths:
      - coverage/

pages:
  stage: build_static
  dependencies:
    - rspec
  script:
    - my coverage/ public/
  artifacts:
    paths:
      - public
    only:
      - master

どのようなjobを実行するか設計 -> jobの集合体をstage
commitするとjobが実行される
build, test, docker_build

stages:
  - build
  - test
  - docker_build

# 全ジョブの前処理
before_script:
  - chmod +x ./gradlew
  - export GRADLE_USER_HOME=`pwd`/.gradlew

# 実行するジョブ
build:
  stage: build
  image: java:openjdk-8u111-jdk-alpine
  script:
    - ./gradlew assemble
  artifacts:
    name: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}"
    paths:
      - build/libs/*.jar

test:
  stage: test
  image: java:openjdk-8u111-jdk-alpine
  script:
    - ./gradlew test sonarqube -PsonarBranch=$CI_COMMIT_REF_NAME -PsonarHostUrl=$SONAR_HOST_UR+

docker_build:
  stage: docker_build
  image: docker:18.03
  script:
    - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY
    - docker build --build-arg JAR_FILE=$(find ./build/libs/ -name "sample*.jar") -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME" ./
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME"

うーん、なるほど、少しずつって感じやな

gitlab tips

### Services: コンテナイメージを複数扱う
servicesというキーワードを用いると、ベースイメージと接続可能なコンテナを定義できる

servies:
  - postgres:latest
variables:
  POSTGRES_DB: custom_db
  POSTGRES_USER: ucstom_user
  POSTGRES_PASSWORD: custom_password

### Anchors

test1:
  stage: test
  image: python:latest
  before_script:
    - pipenv install --dev --system
  script:
    - pytest test1

test2:
  state: test
  image: python:latest
  before_script:
    - pipenv install --dev --system
  script:
    - pytest test2
.test_template: &test_definition
  state: test
  image: python:latest
  before_script:
    - pipenv install --dev --system

test1:
  <<: *test_definition
  script:
    - pytest test1

test2:
  <<: *test_definition
  script:
    - pytest test2  

### 用意されているGitlab変数

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
  script:
    - docker build --pull -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
  only:
    - tags

### only and except
tagがpushされた時のみ
only:
– tags
ブランチがpushされた時は実行しない
except:
– master

あれ、Dockerfileではなく、gitlab.ymlにimageを書くの?

gitlab-ci.yml練習

stages:
  - prepare
  - echo

prepare-job:
  stage: prepare
  script:
    - echo "Prepare bofore echo"

echo-job:
  stage: echo
  script: 
    - echo "Ahoy! This is Gitlab CI/CD!"

stages:
  - dev
  - stg

dev-job:
  stage: dev
  script:
    - echo "Hello, $GITLAB_USER_LOGIN!"
    - echo "This is $CI_COMMIT_BRANCH branch."
    - sleep 5

stg-job:
  stage: stg
  script:
    - echo "hello world!"

$GITLAB_USER_LOGIN, $CI_COMMIT_BRANCH branchは予め用意されている模様

### Docker

default:
  image: ruby:2.7.2

stages:
  - stg

hello-job:
  stage: stg
  script:
    - ruby -v

### artifact

stages:
  - dev

generate-file:
  stage: dev
  artifacts:
    paths:
      - hoge.txt
  script:
    - echo hoge > hoge.txt

なるほど、なんか色々できそうなことはわかった

GitLab Container Registryを使おう

registoryにログインします
$ sudo docker login registry.gitlab.com
Login Succeeded

Dockerfile

FROM alpine:latest

### build
$ sudo docker build -t registry.gitlab.com/hpscript/docker .
$ sudo docker images;
registry.gitlab.com/hpscript/docker latest 9c842ac49a39 41 hours ago 5.57MB
$ sudo docker push registry.gitlab.com/hpscript/docker

なるほどー、なんか勉強になるな

[Docker Hub] イメージを登録する

1. リポジトリを作成する
ddddocker/myexample

2. Dockerイメージ名を調整する
$ sudo docker tag myphpimage ddddocker/myexample:v1

3. リポジトリログイン
$ sudo docker login

4. イメージ登録
$ sudo docker push ddddocker/myexample:v1

なるほど、なんとなく基礎はわかってきた。
あとはdockerfileのベストプラクティスとデプロイCI/CDのところか...

[Docker] イメージの保存と読み込み

– 作成したイメージをファイル化すると、別のコンピュータに持っていくことができる。
– ファイル化にはdocker saveを使う。
– ファイルから取り出すには docker loadを使う。

### docker saveでイメージからファイル化
$ sudo docker save -o saved.tar myphpimage
$ ls -al saved.tar
-rw——- 1 root root 262609920 Mar 24 07:19 saved.tar

内容確認
$ sudo tar tvf saved.tar

### docker loadする
$ sudo docker image rm myphpimage
$ sudo docker load -i saved.tar
$ sudo docker image ls
$ sudo docker stop myphp02
$ sudo docker rm myphp02


export/importによるファイル化
L コンテナ情報が失われるためあまり使用しない

なるほど、Docker hubなどを使わなくても、tarファイルに出来るんだね
中々勉強になるわ…

[Docker] コマンドの実行

コマンドの実行系統の命令は「RUN」「CMDとENTRYPOINT」の2種類ある
前者はイメージ作成時、後者はコマンドの実行時

### RUN
RUNコマンドはdocker buildするタイミング
(1)シェル形式
/bin/sh -cを経由してコマンドが実行
RUN ${コマンド} ${引数} …

(2)exec形式
実行したいコマンドや引数を[]を囲んで記述
RUN [“コマンド”, “引数”, …]

RUNコマンドごとにレイヤーが増えるので、一つのコマンドで実現できる様にする
RUN コマンド1 && コマンド2 && コマンド3

ONBUILD COPY コピー元 コピー先

### CMDとENTRY POINT
コンテナを起動したときのタイミングでコンテナの中で実行するコマンドを指定する
(1)ENTRY POINT: コマンドの指定を強要する。イメージの利用者はこの設定を変更することはできない。
docker runの最後に指定するコマンドは、ENTRYPOINTで指定したコマンドへの引数となる

(2)CMD
docker runの際に指定する最後のコマンドのデフォルト値を変更

殆どの場合、CMDが使われる
イメージが通信しようとするポート番号はEXPOSEで指定する

### HTTPDのDockerfile
https://github.com/docker-library/httpd/blob/master/2.4/Dockerfile

phpimage/index.php

<body>
	Your IP <?php echo $_SERVER['REMOTE_ADDR']; ?>。
</body>

Dockerfile

FROM debian
EXPOSE 80
RUN apt update \
&& apt install -y apache2 php libapache2-mod-php \
&& apt clean \
&& rm -rf /var/lib/apt/lists/* \
&& rm /var/www/html/index.html
COPY index.php /var/www/html
CMD /usr/sbin/apachectl -DFOREGROUND

apt clean, rm -rf /var/lib/apt/lists/* でパッケージを削除する。常套句。
中間ファイルを削除することで、Dockerイメージのサイズを小さくできる
CMD /usr/sbin/apachectl -DFOREGROUND でapacheをフォアグラウンドで実行する

$ sudo docker build . -t myphpimage
$ sudo docker images;
REPOSITORY TAG IMAGE ID CREATED SIZE
myphpimage latest 5c4aa75b77ce 4 minutes ago 254MB

– コンテナを動かす
$ sudo docker run -dit –name myphp -p 8080:80 myphpimage

キャッシュを活用するとビルドを高速化できる
キャッシュを使わない場合は、docker build . -t myphpimage –no-cache とする

なるほど、Dockerfileの作成は練習が必要だな…

[Docker] fileのbuild

Dockerfile

FROM httpd
COPY index.html /usr/local/apache2/htdocs/

1行目はFROM命令
「#」はコメント
行を跨ぐときは末尾に「¥」を記述
「${環境変数}」は環境変数

index.html

<body>
	<h1>Docker Test</h1>
</body>

### イメージをビルド
$ sudo docker build -t myimage01 .
$ sudo docker images;
REPOSITORY TAG IMAGE ID CREATED SIZE
myimage01 latest b2397acb1a5a 43 seconds ago 144MB
$ sudo docker history myimage01;
IMAGE CREATED CREATED BY SIZE COMMENT
b2397acb1a5a About a minute ago /bin/sh -c #(nop) COPY file:752610804eacad0e… 213B

$ sudo docker run -dit –name webcontent_docker -p 8080:80 myimage01
7528440013bfa467f5e06d4f4a0b3eec0182ae5bb433d6045ee52611119f2b7c

http://192.168.56.10:8080/

なるほどー

### Dockerfileのコマンド
FROM
ベースイメージを指定する
ADD
イメージにファイルやフォルダを追加する。Dockerfileを置いたディレクトリ外のリモートファイルも指定できる。圧縮ファイルを指定したときは自動的に展開される。
COPY
イメージにファイルやフォルダを追加する。Dockerfileを置いたディレクトリ内のファイルしか指定できない。圧縮ファイルを指定したときは、圧縮ファイルのままコピーされる。
RUN
イメージをビルドするときにコマンドを実行する。
CMD
コンテナを起動するときに実行する既定のコマンド(docker createやdocker runで実行するコマンドを省略したとき)を指定する
ENTRYPOINT
イメージを実行するとき(docker createやdocker runするとき)のコマンドを強要する
ONBUILD
ビルド完了したときに任意の命令を実行する
EXPOSE
通信を想定するポートをイメージの利用者に伝える
ONBUILD
ビルドが完了したときに任意の命令を実行する
EXPOSE
通信を想定するポートをイメージの利用者に伝える
VOLUME
永続データが保存される場所をイメージの利用者に伝える
ENV
環境変数を定義する
WORKDIR
RUN, CMD, ENTRYPOINT, ADD, COPYの際の作業ディレクトリを指定する
SHELL
ビルド時のシェルを指定する
LABEL
名前やバージョン番号、制作者情報などを設定する
USER
RUN、CMD、ENTRYPOINTで指定するコマンドを実行するユーザやグループを設定する(USERを指定しない場合はroot)
ARG
docker buildする際に指定できる引数を宣言する
STOPSIGNAL
docker stopする際に、コンテナで実行しているプログラムに対して送信するシグナルを変更する(規定は)
HEALTHCHECK
コンテナの死活確認をするヘルスチェックの方法をカスタマイズする