たった1日で基本が身に付く! Docker/Kubernetes超入門 実践 CHAPTER6

SECTION01

SCMとパイプラインを使ったCI/CDの実現

読むだけです。

CI/CDではDockerを多用できる

読むだけです。

Jenkinsについて知ろう

読むだけです。

Jenkinsfileを使ったパイプラインの作り方を知ろう

読むだけです。

SECTION02

環境の全体構成を把握しよう

読むだけです。

Dockerホスト上にJenkinsを用意しよう

ホストは、Chapter2で作成したHost-A、Host-B、Host-Cを使いましょう。

Host-Aを開きます。

Host-Aでdocker composeしましょう。

DockerfileがCentOS用なので、Ubuntu用に・・・と思ったのですが、Ubuntu用は難易度が高いそうなので、Debian用に書き換えましょう。

FROM jenkins/jenkins:lts-jdk11

USER root

# 必要なパッケージと Docker CLI のインストール
RUN apt-get update && apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release \
    && curl -fsSL https://download.docker.com/linux/debian/gpg \
       | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
    && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
       https://download.docker.com/linux/debian bookworm stable" \
       > /etc/apt/sources.list.d/docker.list \
    && apt-get update \
    && apt-get install -y docker-ce-cli \
    && rm -rf /var/lib/apt/lists/*

# Docker Compose と kubectl のインストール
RUN curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose \
    && chmod +x /usr/local/bin/docker-compose \
    && curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/linux/amd64/kubectl \
    && mv kubectl /usr/local/bin/kubectl \
    && chmod +x /usr/local/bin/kubectl

# 秘密鍵とスクリプトの配置
COPY --chown=jenkins:jenkins ./id_rsa /tmp/id_rsa
COPY --chown=jenkins:jenkins ./id_rsa.pub /tmp/id_rsa.pub
COPY ./jenkins.sh /usr/local/bin/jenkins.sh

RUN chmod 0600 /tmp/id_rsa /tmp/id_rsa.pub \
    && chmod 0775 /usr/local/bin/jenkins.sh

USER jenkins

ENV JAVA_OPTS -Djava.awt.headless=true -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Tokyo -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8

EXPOSE 8080 50000

ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/jenkins.sh"]

本にしたがって、Jenkinsを設定しましょう。

その後、本の中で、開発環境と本番環境にSSH接続を、と出てきます。

これは、CHAPTER2で用意していた、Host-BとHost-Cを使いましょう。

CHAPTER2のAnsibleを使い、JenkinsからHost-BとHost-Cに接続できるようにします。

※Ansibleがなかなか動かないので、手動で進めていきます。

CHAPTER2の手順に従って進めましょう。

ただ、使うファイルをjenkinsコンテナにコピーしておく必要があります。

# docker cp /mnt/c/GitHub/docker-book/chap2/ansible/dockerhost jenkins:/tmp

jenkinsコンテナには以下でログインできます。

# docker exec -it --user root jenkins bash

CHAPTER2のページを見て、sshのインストールなどをしてきてください。ansibleの実行は不要です。

SSHのポートは、念のため、2228にしておきましょう。

その後、cdしておきましょう。

# cd /tmp/dockerhost

鍵ファイルのコピーを忘れがちです。

# cp id_rsa ~/.ssh
# cp id_rsa.pub ~/.ssh

鍵ファイルをssh-copy-idします。

# ssh-copy-id -p 2228 root@localhost

下記の追記が必要でした。

# vi ~/.ssh/config
Host jenkins
  HostName localhost
  Port 2228
  User root

jenkinsコンテナから見たHost-B、Host-CのIPアドレスを調べます。

が、ipコマンドがないので、まず、インストールします。

# apt-get update
# apt-get install -y iproute2

jenkinsコンテナから見たホストHost-AのIPを確認します。

※Host-B、Host-Cも同じIPアドレスです。

# ip route | grep default
default via 172.19.0.1 dev eth0

jenkinsコンテナからHost-B、Host-Cへの接続確認をします。

# ssh root@172.19.0.1 -p 2226
# ssh root@172.19.0.1 -p 2227

もし、Connection refusedが出たら、sshdが起動してないかもしれないので、そのホストに行って、sshdを起動します。

Host-B
# sudo /usr/sbin/sshd -p 2226

Host-C
# sudo /usr/sbin/sshd -p 2227

sshをホスト名でできるようにするため、configの設定を見直します。

# vi ~/.ssh/config
Host Host-A
  HostName 172.19.0.1
  Port 2225
  User root
Host Host-B
  HostName 172.19.0.1
  Port 2226
  User root
Host Host-C
  HostName 172.19.0.1
  Port 2227
  User root
Host jenkins
  HostName localhost
  Port 2228
  User root

下記でsshできるようになります。

# ssh Host-A
# ssh Host-B
# ssh Host-C

ただ、docker containerコマンドでsshするときは、下記のようにする必要があります。

# docker container exec -it jenkins ssh root@172.19.0.1 -p 2226
# docker container exec -it jenkins ssh root@172.19.0.1 -p 2227

下記は失敗します。

# docker container exec -it jenkins ssh Host-B
# docker container exec -it jenkins ssh Host-C

せっかく設定したのに悲しいですが、これはなぜかというと、名前解決は恐らくjenkinsコンテナのホストHost-Aの設定を見に行きます。

Host-Aの設定では、localhostにしています。

jenkinsコンテナから見た場合は、172.19.0.1です。

この差により、エラーになります。

もちろん、Host-Aの設定も、172.19.0.1にすれば、通るはずです。

本にある通り、DockerHubへのログインもしておきます。

※本のコマンドに、不要な文字列”ssh”があるので、削除しましょう。

# docker container exec -it jenkins docker login

さあ、ようやく準備が終わりました。

GitHubリポジトリを作成しよう

本にはdocker-kvsをコピーし、とあるが、どれか分かりにくいですね。

ダウンロードしたサンプルファイルの、\chap6\c6cicdのことのようです。

CHAPTER4で使ったdocker-kvsフォルダの中にファイルが2つあるので、それらを削除します。

\chap6\c6cicdの中身を、docker-kvsフォルダの中にコピーし、Commit/Pushし、GitHub上で確認しておきましょう。

Jenkinsfileを確認しよう

本にある通り、環境変数は、自分の環境に合わせて書き換えましょう。

筆者の場合は、下記のようになります。

    DOCKERHUB_USER = "rasken2003"
    BUILD_HOST = "root@172.19.0.1:2226"
    PROD_HOST = "root@172.19.0.1:2227"

Jenkinsのジョブを作成しよう

本にしたがって進めましょう。

スケジュール欄ですが、「*****」ではなく「* * * * *」(間に半角スペース)です。

この設定で、毎分起動するようになります。

さらに、ビルドするブランチは、「master」ではなく、「main」に変更しましょう。

これは、GitHubのデフォルトが変わったためです。

SECTION03

手動ビルドでパイプラインを実行してみる

きっと何かしら失敗するでしょう。

失敗したら、永続リンクにあるリンクをクリックし、左メニューからPipeline Consoleを開きましょう。

見やすく表示されていると思います。

※実行中でも、左下のところから表示できます。

筆者の場合、編集したJenkinsfileをCommit/Pushしていなかったため、変数が古いまま実行したため、エラーになりました。

が、さらにエラーが出ました。

Jenkinsfileで、docker-composeを使っていて、古いからダメだそうです。

docker composeに変えました。

以下の通りです。

忘れずに、コミット&プッシュしましょう。

※次のところで出る、ポート問題の対応も済んでいます。

pipeline {
  agent any
  environment {
    DOCKERHUB_USER = "rasken2003"
    BUILD_HOST = "root@172.19.0.1:2226"
    PROD_HOST = "root@172.19.0.1:2227"
    BUILD_TIMESTAMP = sh(script: "date +%Y%m%d-%H%M%S", returnStdout: true).trim()
  }
  stages {
    stage('Pre Check') {
      steps {
        sh "test -f ~/.docker/config.json"
        sh "cat ~/.docker/config.json | grep docker.io"
      }
    }
    stage('Build') {
      steps {
        sh "cat docker-compose.build.yml"
        sh "DOCKER_HOST=ssh://${BUILD_HOST} docker compose -f docker-compose.build.yml down"
        sh "docker -H ssh://${BUILD_HOST} volume prune -f"
        sh "DOCKER_HOST=ssh://${BUILD_HOST} docker compose -f docker-compose.build.yml build"
        sh "DOCKER_HOST=ssh://${BUILD_HOST} docker compose -f docker-compose.build.yml up -d"
        sh "DOCKER_HOST=ssh://${BUILD_HOST} docker compose -f docker-compose.build.yml ps"
      }
    }
    stage('Test') {
      steps {
        sh "docker -H ssh://${BUILD_HOST} container exec dockerkvs_apptest pytest -v test_app.py"
        sh "docker -H ssh://${BUILD_HOST} container exec dockerkvs_webtest pytest -v test_static.py"
        sh "docker -H ssh://${BUILD_HOST} container exec dockerkvs_webtest pytest -v test_selenium.py"
        sh "DOCKER_HOST=ssh://${BUILD_HOST} docker compose -f docker-compose.build.yml down"
      }
    }
    stage('Register') {
      steps {
        sh "docker -H ssh://${BUILD_HOST} tag dockerkvs_web ${DOCKERHUB_USER}/dockerkvs_web:${BUILD_TIMESTAMP}"
        sh "docker -H ssh://${BUILD_HOST} tag dockerkvs_app ${DOCKERHUB_USER}/dockerkvs_app:${BUILD_TIMESTAMP}"
        sh "docker -H ssh://${BUILD_HOST} push ${DOCKERHUB_USER}/dockerkvs_web:${BUILD_TIMESTAMP}"
        sh "docker -H ssh://${BUILD_HOST} push ${DOCKERHUB_USER}/dockerkvs_app:${BUILD_TIMESTAMP}"
      }
    }
    stage('Deploy') {
      steps {
        sh "cat docker-compose.prod.yml"
        sh "echo 'DOCKERHUB_USER=${DOCKERHUB_USER}' > .env"
        sh "echo 'BUILD_TIMESTAMP=${BUILD_TIMESTAMP}' >> .env"
        sh "echo 'WEB_PORT=8082' >> .env"
        sh "cat .env"
        sh "DOCKER_HOST=ssh://${PROD_HOST} docker compose -f docker-compose.prod.yml up -d"
        sh "DOCKER_HOST=ssh://${PROD_HOST} docker compose -f docker-compose.prod.yml ps"
      }
    }
  }
}

でもまた、エラーが出ました。

Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint dockerkvs_web (f7034784af6bf8ab1acee4f29721691b8151f492e8f9d78293c09f9040ff2232): failed to bind host port for 0.0.0.0:80:172.20.0.6:80/tcp: address already in use

ポートが競合してるってことです。

本来なら、サーバーは別に存在するから、こういうことに苦労しないのでしょうが、お試しで1つの環境でやっているから、仕方ないですね。

docker-compose-build.ymlを下記のように書き換えます。

80:80

とあるところを、

8081:80

にします。

docker-compose.prod.ymlも書き換えます。

80:80

とあるところを、

"${WEB_PORT:-8082}:80"

にします。

※環境変数は、Jenkinsfileで設定しています。

さあ、コミット&プッシュして、ジョブ実行です。

ようやく動き・・・ダメでした。

テストのところで、appに接続失敗しています。

これはかなりつまずきました。

wait-for-it.shというものを入れて、ウェイトをしてみました。

これは、appが立ち上がる前に、apptestが動いてしまっているのでは、という疑いがあるためです。

ダウンロードします。

# cd /mnt/c/github/docker-kvs
# curl -o wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh
# chmod +x wait-for-it.sh

上記の対応は無意味でした。appは立ち上がっていませんでした。

本当の原因は、またも、Flaskのバージョンが古い問題でした。

これがなぜかJenkinsのログに出てこないのです。

どうも、Flaskのバージョンが古いゆえ、らしいですが、本当のところは不明です。

/docker-kvs/app/Dockerfile

1.1.1になっているので、2.2.3に書き換えてください。

Jenkinsfileは対応済みのものをすでに用意していたので、このまま続行してください。

最後に、DockerHubへのPushでエラーが。

なんと、無料範囲を超えているとか。

2024年あたりから、プライベートは1個までになったそうです。

パブリックに変えればいいようです。

やっと、成功しました。

アプリを動作確認したいところですが、本にしたがって、先に進みます。

ビルドの履歴を確認してみる

これはもう、何度もやっているでしょうから、いいですね。

読むだけです。

SCMを更新して自動でパイプラインを実行させよう

タイトルが「DockerKVS Service」になっていると書いてありますが、すでに「Hello Service」に変更されています。

なんでもいいので、例えば、「Hello Service2」にしておきましょう。

筆者はテスト1回目は失敗しました。

設定ミスをしていました。(GitのURLの最後の/がなかったため)

エラーの確認は、Gitのポーリングログや設定でできます。

Jenkinsが、バージョン古いかのようなメッセージを出し続けていたので、最新にしました。

\chap6\c6jenkins\Dockerfile

FROM jenkins/jenkins:jdk21

その後、何度やっても、エラーが出てしまいましたが、コンテナの重複エラーで、きちんとコンテナを削除していなかったためです。

各ホストで、

# docker container prune

を実行しましょう。

これでようやく・・・と思ったのですが、下記のエラーが出ました。

前は出なかったと思うんですけどね。

+ DOCKER_HOST=ssh://root@172.19.0.1:2227 docker compose -f docker-compose.prod.yml up -d
time="2025-04-28T05:02:34Z" level=warning msg="/var/jenkins_home/workspace/docker-kvs@2/docker-compose.prod.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion"
unable to get image 'redis:5.0.6-alpine3.10': error during connect: Get "http://docker.example.com/v1.49/images/redis:5.0.6-alpine3.10/json": command [ssh -o ConnectTimeout=30 -T -l root -p 2227 -- 172.19.0.1 docker system dial-stdio] has exited with exit status 255, make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=ssh: connect to host 172.19.0.1 port 2227: Connection refused
script returned exit code 1

これは、単純に、sshが起動していなかっただけでした。

# sudo mkdir -p /run/sshd
# /usr/sbin/sshd -p 2227

あと、1分おきだと早すぎる(ビルドに3分くらいかかるので、どうしても重複してしまう)ので、5分に1回に変更しました。

*/5 * * * *

ようやく自動実行が成功しました。

Jenkinsfileの処理を見てみよう

大事な内容ですが、読むだけです。

パイプライン処理1:変数の定義

大事な内容ですが、読むだけです。

パイプライン処理2:環境などの事前チェック

大事な内容ですが、読むだけです。

パイプライン処理3:ビルド環境でイメージを作成

大事な内容ですが、読むだけです。

パイプライン処理4:ビルド環境でユニットテストを実行

大事な内容ですが、読むだけです。

パイプライン処理5:イメージをレジストリに登録

大事な内容ですが、読むだけです。

パイプライン処理6:本番環境で本番用イメージを自動展開

大事な内容ですが、読むだけです。

CHAPTER6は以上になります。

たった1日で基本が身に付く! Docker/Kubernetes超入門 実践 CHAPTER6」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。