#author("2020-10-06T22:33:33+09:00","default:jomura","jomura")
#contents

#br

* Redmineの導入 [#wa722c9e]

 Redmineについては、下記3種の導入方法を検討した。
+ 公式サイトの本体コード一式 + RHELのRPM + gemコマンドによるライブラリ導入
-- 各々のライブラリ依存性管理が非常に大変。特にnokogiriとopensslが鬼門。
+ bitnami-redmineを用いた一括導入
-- Git, Redmine, PhpMyAdminなども導入されるが、今回は使わない
-- Redmine公式ではない
+ 公式dockerイメージを用いた導入
-- dockerを意識して維持管理する手間が生じる
-- ライブラリ間の依存性に悩む必要がなくなる
-- cf. https://qiita.com/bezeklik/items/b9d75ee74e0ae4c6d42c

 今回は「公式dockerイメージを用いた導入」を採用する。

** 導入サーバの前提条件 [#la5e25ea]
+ OSはRHEL7(or CentOS7)とする。RHEL8にはdockerイメージが対応していない?
+ 事前導入するソフトウェアパッケージは最小構成。
+ インターネットに接続可能   ※必要ならproxy設定
 /etc/yum.conf
 proxy=http://proxy.jomura.net:8080/     #as your own
 ~/.bashrc
 export HTTP_PROXY=http://proxy.jomura.net:8080/     #as your own
 export HTTPS_PROXY=${HTTP_PROXY}
+ パッケージの更新が事前に実行されている
 yum clean all && yum -y update && reboot
+ Redmine Webサイト用のFully Qualified Domain Name(FQDN)が用意されている。

** Redmine用 configuration.ymlの作成 [#z6fa156d]
- Ansibleサーバ上の一般ユーザで実行
- SMTP情報は as your own で
 cat << "_EOF_" > configuration.yml
 default:
   email_delivery:
     delivery_method: :smtp
     smtp_settings:
 #      tls: true
       enable_starttls_auto: true
       address: "smtp.gmail.com"
       port: 587
       domain: "jomura.net"
       authentication: :login
       user_name: "user@jomura.net"
       password: "********"
 _EOF_

** Redmine初期設定用SQLの作成 [#t656e6ad]
- Ansibleサーバ上の一般ユーザで実行
 cat << "_EOF_" > settings.sql
  INSERT INTO `settings` (`name`, `value`) VALUES
  ('search_results_per_page','30'), -- ページごとの検索結果表示件数 (10)
  ('host_name','cxits.tg-group.tokyo-gas.co.jp'), -- ホスト名とパス (localhost:3000)
  ('protocol','http'), -- プロトコル (http)
  ('text_formatting','textile'), -- テキスト書式 (textile)
  ('default_language','ja'), -- デフォルトの言語 (en)
  ('force_default_language_for_anonymous','0'), -- 匿名ユーザーにデフォルトの言語を強制 (0)
  ('force_default_language_for_loggedin','0'), -- ログインユーザーにデフォルトの言語を強制 (0)
  ('user_format','lastname_firstname'), -- ユーザー名の表示形式 (firstname_lastname)
  ('thumbnails_enabled','1'), -- 添付ファイルのサムネイル画像を表示 (0)
  ('login_required','1'), -- 認証が必要 (0)
  ('autologin','7'), -- 自動ログイン (0)
  ('max_additional_emails','3'), -- 追加メールアドレス数の上限 (5)
  ('session_lifetime','86400'), -- 有効期間の最大値 (0)
  ('session_timeout','240'), -- 無操作タイムアウト (0)
  ('default_users_time_zone','Tokyo'), -- タイムゾーン ()
  ('rest_api_enabled','1'), -- RESTによるWebサービスを有効にする (0)
  ('jsonp_enabled','1'), -- JSONPを有効にする (0)
  ('default_projects_public','0'), -- デフォルトで新しいプロジェクトは公開にする (1)
  ('default_projects_modules','---
  - issue_tracking
  - time_tracking
  - wiki
  - repository
  - calendar
  - gantt
  '), -- 新規プロジェクトにおいてデフォルトで有効になるモジュールチケットトラッキング
  ('default_projects_tracker_ids','---
  - \'1\'
  - \'2\'
  - \'3\'
  '), -- 新規プロジェクトにおいてデフォルトで有効になるトラッカー
  ('cross_project_issue_relations','1'), -- 異なるプロジェクトのチケット間で関連の設定を許可 (0)
  ('default_issue_start_date_to_creation_date','1'), -- 現在の日付を新しいチケットの開始日とする (1)
  ('issue_done_ratio','issue_status'), -- 進捗率の算出方法 (issue_field)
  ('issue_list_default_totals','---
  - estimated_hours
  - spent_hours
  '), -- チケットの一覧で表示する項目(合計)
  ('attachment_max_size','51200'), -- 添付ファイルサイズの上限 (5120)
  ('repositories_encodings','utf-8,cp932,euc-jp'), -- 添付ファイルとリポジトリのエンコーディング ()
  ('mail_from','user@jomura.net'), -- 送信元メールアドレス (redmine@example.net)
  ('enabled_scm','---
  - Git
  '), -- 使用するバージョン管理システム
  ('commit_ref_keywords','refs,references,IssueID,*'), -- 参照用キーワード (refs,references,IssueID)
  ('commit_cross_project_ref','1'); -- 異なるプロジェクトのチケットの参照/修正を許可 (0)
 _EOF_

** docker-compose.ymlファイルの作成 [#z4d0976f]
- Ansibleサーバ上の一般ユーザで実行
 cat << "_EOF_" > docker-compose.yml
  version: '3.7'
  services:
    redmine:
      container_name: ${REDMINE:-redmine}
      image: redmine:4-passenger
      restart: always
      ports:
        - 3000:3000
      depends_on:
        - ${REDMINE_DB_MYSQL:-mysql}
        - ${REDMINE_MEMCACHED:-memcached}
      environment:
        TZ: ${TZ}
        REDMINE_DB_MYSQL: ${REDMINE_DB_MYSQL:-mysql}
        REDMINE_DB_DATABASE: ${REDMINE_DB_DATABASE}
        REDMINE_DB_USERNAME: ${REDMINE_DB_USERNAME}
        REDMINE_DB_PASSWORD: ${REDMINE_DB_PASSWORD}
        REDMINE_DB_ENCODING: ${REDMINE_DB_ENCODING:-utf8mb4}
 {% if proxy_env.http_proxy is defined %}
        HTTP_PROXY: {{ proxy_env.http_proxy }}
        HTTPS_PROXY: {{ proxy_env.https_proxy }}
 {% endif %}
      volumes:
        - ${REDMINE_PATH:-.}/config/additional_environment.rb:/usr/src/redmine/config/additional_environment.rb
        - ${REDMINE_PATH:-.}/config/configuration.yml:/usr/src/redmine/config/configuration.yml
        - ${REDMINE_PATH:-.}/Gemfile.local:/usr/src/redmine/Gemfile.local
        - ${REDMINE_PATH:-.}/files:/usr/src/redmine/files:z
        - ${REDMINE_PATH:-.}/log:/usr/src/redmine/log:Z
        - ${REDMINE_PATH:-.}/plugins:/usr/src/redmine/plugins
        - ${REDMINE_PATH:-.}/public/themes:/usr/src/redmine/public/themes
        - /var/lib/git:/var/lib/git:z
    mysql:
      container_name: ${REDMINE_DB_MYSQL:-mysql}
      image: mysql:8
      command: --default-authentication-plugin=mysql_native_password
      restart: always
      ports:
        - 3306:3306
      environment:
        TZ: ${TZ}
        MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
        MYSQL_DATABASE: ${REDMINE_DB_DATABASE}
        MYSQL_USER: ${REDMINE_DB_USERNAME}
        MYSQL_PASSWORD: ${REDMINE_DB_PASSWORD}
      volumes:
        - mysql-data:/var/lib/mysql
        - ${REDMINE_PATH:-.}/../mysql/conf.d/redmine.cnf:/etc/mysql/conf.d/redmine.cnf
    memcached:
      container_name: ${REDMINE_MEMCACHED:-memcached}
      image: memcached
      restart: always
  volumes:
    mysql-data:
      name: mysql-data
 _EOF_

** docker用環境変数ファイルの作成 [#mef56985]
- Ansibleサーバ上の一般ユーザで実行
 cat << _EOF_ > docker-env
 REDMINE_PATH={{ redmine_path }}
 TZ=Asia/Tokyo
 MYSQL_ROOT_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!$%&()*+,-./:;<=>?@[\]^_{|}~' | head -c 16; echo)
 REDMINE_DB_MYSQL=mysql
 REDMINE_DB_DATABASE=redmine
 REDMINE_DB_USERNAME=redmine
 REDMINE_DB_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!$%&()*+,-./:;<=>?@[\]^_{|}~' | head -c 16; echo)
 REDMINE_DB_ENCODING=utf8mb4
 REDMINE_MEMCACHED=memcached
 _EOF_
- catに指定する _EOF_ を"でくくってはいけない。urandamコマンド等が実行されなくなる。

** playbookの作成 [#y22e4e40]
- Ansibleサーバ上の一般ユーザで実行
- sudoの場合、become_method: sudo
 cat << "_EOF_" > pb_redmine_server-1.yml
 # install redmine
 
 - hosts: redmine_servers
   become: true
   become_method: su
   environment: "{{ proxy_env }}"
 
   vars:
     redmine_path: /srv/redmine
 
   tasks:
 
     - name: check facts
       fail:
         msg: "Not compatible with [{{ ansible_os_family }}] {{ ansible_distribution }} {{ ansible_distribution_major_version }}."
       when: ansible_os_family != 'RedHat' or ansible_distribution_major_version|int != 7
 
     - name: install RPMs
       yum:
         name:
           - yum-utils
           - httpd
         state: latest
 
     - name: install docker
       shell: yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
       changed_when: False
     - yum: name=docker-ce state=latest
       notify:
         - restart Docker
 
     - name: set proxy for docker
       file: path=/etc/systemd/system/docker.service.d state=directory
       when: proxy_env.http_proxy is defined
     - copy:
         dest: /etc/systemd/system/docker.service.d/http-proxy.conf
         force: no
         content: "[Service]\n Environment = \"http_proxy={{ proxy_env.http_proxy }}\" \"https_proxy={{ proxy_env.https_proxy }}\"\n "
       when: proxy_env.http_proxy is defined
 
     - meta: flush_handlers
 
     - name: install docker-compose
       stat: path=/usr/local/bin/docker-compose
       register: result01
     - shell: |
         curl --location --output /usr/local/bin/docker-compose \
           $(curl --silent --show-error \
             https://api.github.com/repos/docker/compose/releases/latest \
             | grep 'Linux-x86_64"' \
             | grep url \
             | cut --delimiter='"' --fields=4 \
            )
         chmod +x /usr/local/bin/docker-compose
       args:
         warn: false
       when: result01.stat.exists == false
 
     - name: "create {{ redmine_path }}"
       file: path={{ redmine_path }} state=directory
       register: result02
     - shell: |
         /usr/sbin/matchpathcon {{ redmine_path }}
         /usr/sbin/semanage fcontext --add --type container_file_t {{ redmine_path }}
         /usr/sbin/restorecon -v {{ redmine_path }}
         /usr/sbin/matchpathcon {{ redmine_path }}
       args:
         warn: false
       when: result02.changed == true
 
     - name: create config files
       file: path={{ redmine_path }}/config state=directory
     - copy:
         src: configuration.yml
         dest: "{{ redmine_path }}/config/configuration.yml"
         force: no
     - copy:
         dest: "{{ redmine_path }}/config/additional_environment.rb"
         force: no
         content: |
           config.cache_store = :mem_cache_store, "memcached"
           config.logger = Logger.new("#{Rails.root}/log/#{ENV['RAILS_ENV']}.log", 50, 1000000)
           config.logger.level = Logger::INFO
     - copy:
         dest: "{{ redmine_path }}/Gemfile.local"
         force: no
         content: "gem 'dalli'\n "
     - file: path={{ redmine_path }}/../mysql/conf.d state=directory
     - copy:
         dest: "{{ redmine_path }}/../mysql/conf.d/redmine.cnf"
         force: no
         content: |
           [mysqld]
           innodb_buffer_pool_size = 536870912
           innodb_log_file_size = 201326592
 
     - name: create a parent dir of git-repos
       file:
         path: /var/lib/git
         owner: nobody
         group: users
         state: directory
         mode: 02775
       register: result03
     - shell: |
         /usr/sbin/semanage fcontext -a -t httpd_git_content_t "/var/lib/git(/.*)?"
         /usr/sbin/restorecon -Rv /var/lib/git
       args:
         warn: false
       when: result03.changed == true
 
     - name: docker-compose up
       template:
         src: docker-compose.yml
         dest: "{{ redmine_path }}/docker-compose.yml"
         force: no
     - template:
         src: docker-env
         dest: "{{ redmine_path }}/.env"
         force: no
         mode: 0400
     - shell: docker-compose --project-directory {{ redmine_path }} up --detach
       args:
         chdir: "{{ redmine_path }}"
       register: result04
       changed_when: '" is up-to-date" not in result04.stderr'
     - name: wait for Completed 200 OK
       shell: docker container logs redmine 2>/dev/null | tail -15
       register: result05
       changed_when: false
       until: '"Completed 200 OK " in result05.stdout'
       retries: 100
       delay: 5
 
     - name: set db password file
       slurp:
         src: "{{ redmine_path }}/.env"
       register: result06
     - copy:
         dest: ~/.my.cnf.org
         force: no
         content: "[client]\n user = redmine\n password = {{ result06['content'] | b64decode | regex_findall('REDMINE_DB_PASSWORD=(.+)\\n ') | first }}\n host = localhost\n "
       register: result07
     - shell: docker cp ~/.my.cnf.org mysql:root/.my.cnf
       when: result07.changed
 
     - name: load default data
       shell: docker exec {% if proxy_env.http_proxy is defined -%}
         -e HTTP_PROXY={{ proxy_env.http_proxy }} -e HTTPS_PROXY={{ proxy_env.https_proxy }} {% endif -%}
         redmine bundle exec rake redmine:load_default_data RAILS_ENV=production REDMINE_LANG=ja
       register: result08
       changed_when: '" is already loaded." not in result08.stdout'
 
     - name: update roles
       shell: |
         cat << '_EOQ_' | docker exec -i mysql mysql redmine
         UPDATE `roles` SET `permissions` = NULL WHERE `id` = '1' OR `id` = '2';
         _EOQ_
       changed_when: false
 
     - name: insert settings
       shell: |
         cat << '_EOQ_' | docker exec -i mysql mysql redmine
         SELECT count(*) FROM `settings`
         _EOQ_
       changed_when: false
       register: result09
     - shell: |
         cat settings.sql | docker exec --interactive mysql mysql redmine && \
         docker exec redmine passenger-config restart-app /usr/src/redmine
       when: result09.stdout_lines[1] == "0"
 
     - name: clear rails cache
       shell: docker exec {% if proxy_env.http_proxy is defined -%}
         -e HTTP_PROXY={{ proxy_env.http_proxy }} -e HTTPS_PROXY={{ proxy_env.https_proxy }} {% endif -%}
         redmine bundle exec rails runner 'Rails.cache.clear'
       changed_when: false
 
     - name: bundle install
       shell: docker exec {% if proxy_env.http_proxy is defined -%}
         -e HTTP_PROXY={{ proxy_env.http_proxy }} -e HTTPS_PROXY={{ proxy_env.https_proxy }} {% endif -%}
         redmine bundle install
       changed_when: false
 
     - name: restart passenger
       shell: docker exec {% if proxy_env.http_proxy is defined -%}
         -e HTTP_PROXY={{ proxy_env.http_proxy }} -e HTTPS_PROXY={{ proxy_env.https_proxy }} {% endif -%}
         redmine passenger-config restart-app /usr/src/redmine
       changed_when: false
 
     - name: modify httpd.conf for redmine
       copy:
         dest: /etc/httpd/conf.d/proxy-redmine.conf
         force: no
         mode: 0644
         content: |
           <IfModule !proxy_module>
             LoadModule proxy_module modules/mod_proxy.so
           </IfModule>
           <IfModule !proxy_http_module>
             LoadModule proxy_http_module modules/mod_proxy_http.so
           </IfModule>
           ProxyPassMatch /git.* !
           ProxyPass / http://localhost:3000/
           ProxyPassReverse / http://localhost:3000/
       register: result10
     - shell: /usr/sbin/setsebool -P httpd_can_network_connect 1
       when: result10.changed == true
       notify:
         - restart Apache
 
     - name: open ports
       firewalld:
         service: "{{ item }}"
         permanent: true
         state: enabled
         immediate: yes
       loop:
         - http
         - https
 
   handlers:
 
     - name: restart Docker
       systemd:
         name: docker
         state: restarted
         daemon_reload: yes
         enabled: yes
 
     - name: restart Apache
       systemd:
         name: httpd
         state: restarted
         daemon_reload: yes
         enabled: yes
 _EOF_
- docker_composeはRPMパッケージではなく最新版を使う

** playbookの実行 [#x17c57c6]
- Ansibleサーバ上の一般ユーザで実行
- 実行前に文法チェックしよう
 ansible-playbook -i inventory.yml pb_redmine_server-1.yml --syntax-check
- 問題なければ実効
 ansible-playbook -i inventory.yml pb_redmine_server-1.yml


* Gitの導入 [#af224b39]

 GitLabは「Redmineとの連携は結構面倒」なのと「リソース(主にメモリ)を大量に消費し、しばしば動作不安定を招くほど」との評判があることから、導入を断念。CGI"git-http-backend"とRedmine.pmを用いた、従来どおりのGitリポジトリサービスを選択。ついでに、GitWebも導入。

** Apache用設定ファイルの作成 [#f8c6ab05]
- Ansibleサーバ上の一般ユーザで実行
 cat << "_EOF_" > gitrepos.conf
 <IfModule mod_rewrite.c>
     RewriteEngine On
 
     RewriteCond %{REQUEST_URI} ^/git/?$
     RewriteRule ^/git /gitweb [R=301,L]
 #    RewriteRule ^/git/$ /gitweb/ [L,R]
 </IfModule>
 
 PerlLoadModule Apache::Authn::Redmine
 
 SetEnv GIT_PROJECT_ROOT /var/lib/git
 SetEnv GIT_HTTP_EXPORT_ALL
 
 ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
 
 #<IfModule mod_rewrite.c>
 #   RewriteEngine On
 #   RewriteCond %{HTTPS} ^off$
 #   RewriteRule ^/git/(.*)$ /usr/libexec/git-core/git-http-backend/$1 [E=REMOTE_USER:$REDIRECT_REMOTE_USER,H=cgi-script,L]
 #</IfModule>
 
 #<Directory /usr/libexec/git-core>
 #   <Files "git-http-backend">
 #      Options +ExecCGI
 #   </Files>
 #</Directory>
 
 <Location /git/>
   Order allow,deny
   Allow from all
 
   PerlAccessHandler Apache::Authn::Redmine::access_handler
   PerlAuthenHandler Apache::Authn::Redmine::authen_handler
 
   AuthType Basic
   AuthName Git
 #  <LimitExcept GET PROPFIND OPTIONS REPORT>
 #    SSLRequireSSL
 #  </LimitExcept>
 
   # for Redmine Authentication
   RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
   RedmineDbUser "redmine"
   RedmineDbPass ""
   RedmineGitSmartHttp yes
 
   Require valid-user
 
 #  SetEnvIf Request_URI "^/git/$" allow
 #  Order allow,deny
 #  Allow from env=allow
 #  Satisfy any
 </Location>
 _EOF_

** playbookの作成 [#t40d7aae]
- Ansibleサーバ上の一般ユーザで実行
- sudoの場合、become_method: sudo
 cat << "_EOF_" > pb_redmine_server-2.yml
 # install git
 # dependencies: pb_redmine_server-1.yml
 
 - hosts: redmine_servers
   become: true
   become_method: su
   environment: "{{ proxy_env }}"
 
   vars:
     redmine_path: /srv/redmine
 
   tasks:
 
     - name: check facts
       fail:
         msg: "Not compatible with [{{ ansible_os_family }}] {{ ansible_distribution }} {{ ansible_distribution_major_version }}."
       when: ansible_os_family != 'RedHat' or ansible_distribution_major_version|int != 7
 
     - name: check redmine
       stat: path={{ redmine_path }}
       register: result01
     - fail:
         msg: "pb_redmine_server.yml has been executed yet."
       when: result01.stat.exists == false
 
     - name: install RPMs
       yum:
         name: 
           - httpd
           - git
           - mod_perl
           - perl-Digest-SHA
           - perl-DBI
           - perl-DBD-mysql
           - gitweb
         state: latest
 
     - name: modify apache user for git
       user: name=apache groups=users append=yes
       notify:
         - restart Apache
     - copy:
         dest: /etc/systemd/system/httpd.service
         force: no
         content: ".include /lib/systemd/system/httpd.service\n[Service]\nUMask=002\n"
       notify:
         - restart Apache
 
     - name: copy Redmine.pm
       file: path=/etc/httpd/Apache/Authn state=directory
     - shell: docker cp redmine:/usr/src/redmine/extra/svn/Redmine.pm /etc/httpd/Apache/Authn/Redmine.pm
 
     - name: create gitrepos.conf
       copy:
         src: gitrepos.conf
         dest: /etc/httpd/conf.d/gitrepos.conf
         force: no
         mode: 0640
     - slurp:
         src: "{{ redmine_path }}/.env"
       register: result03
     - lineinfile:
         dest: /etc/httpd/conf.d/gitrepos.conf
         regexp: "^  RedmineDbPass"
         line: "  RedmineDbPass {{ result03['content'] | b64decode | regex_findall('REDMINE_DB_PASSWORD=(.+)\\n') | first }}"
 
     - name: modify gitweb
       lineinfile:
         dest: /etc/httpd/conf.d/git.conf
         regexp: '^Alias /git /var/www/git$'
         line: 'Alias /gitweb /var/www/git'
 
   handlers:
 
     - name: restart Apache
       systemd:
         name: httpd
         state: restarted
         daemon_reload: yes
         enabled: yes
 _EOF_

** playbookの実行 [#ie6760c5]
- Ansibleサーバ上の一般ユーザで実行
 ansible-playbook -i inventory.yml pb_redmine_server-2.yml

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS